started adding settings window for ROM paths
cycle counts accurate when crossing page boundaries lots of other improvements
This commit is contained in:
parent
792386aceb
commit
7235d3a3bb
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24627.0
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "6502EmulatorFrontend", "6502EmulatorFrontend\6502EmulatorFrontend.csproj", "{C198497A-3D3E-4F01-A72B-642DAB6B11D6}"
|
||||
EndProject
|
||||
|
@ -24,13 +24,4 @@ Global
|
|||
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
|
||||
|
|
|
@ -18,6 +18,21 @@
|
|||
<SccLocalPath>SAK</SccLocalPath>
|
||||
<SccAuxPath>SAK</SccAuxPath>
|
||||
<SccProvider>SAK</SccProvider>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -65,7 +80,12 @@
|
|||
<Compile Include="Apple2Display.cs" />
|
||||
<Compile Include="cpu\Disassembly.cs" />
|
||||
<Compile Include="IDisplay.cs" />
|
||||
<Compile Include="MainWindowEvents.cs" />
|
||||
<Compile Include="MainWindowViewModel.cs" />
|
||||
<Compile Include="SettingsWindow.xaml.cs">
|
||||
<DependentUpon>SettingsWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SettingsWindowViewModel.cs" />
|
||||
<Compile Include="video\CharacterBitmap.cs" />
|
||||
<Compile Include="video\CharacterSet.cs" />
|
||||
<Page Include="MainWindow.xaml">
|
||||
|
@ -84,6 +104,10 @@
|
|||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="SettingsWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
|
@ -115,6 +139,18 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="memory\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.6">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.6 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</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.
|
||||
|
|
|
@ -42,35 +42,35 @@ namespace _6502EmulatorFrontend
|
|||
public byte DSPCR;
|
||||
};
|
||||
|
||||
[DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/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)]
|
||||
[DllImport("C:/apple/fruitmachine/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void putKeyInBuffer(byte key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,55 +5,64 @@
|
|||
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"/>
|
||||
Title="Fruit Machine 0.0000001a" Height="700" Width="954">
|
||||
<DockPanel>
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="_File"/>
|
||||
<MenuItem Header="_Settings">
|
||||
<MenuItem Header="_ROM Paths" Click="MenuItem_Click"/>
|
||||
</MenuItem>
|
||||
|
||||
</Menu>
|
||||
<Grid>
|
||||
<Button x:Name="btnLoadBinary" Content="Load Binary" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top" Width="75" Click="btnLoadBinary_Click"/>
|
||||
<Label x:Name="binaryLoadedStatus" Content="Label" HorizontalAlignment="Left" Margin="90,108,0,0" VerticalAlignment="Top" Width="417"/>
|
||||
<Grid HorizontalAlignment="Left" Height="185" Margin="10,27,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,217,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,0,0,0" VerticalAlignment="Top" Width="640" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
|
||||
<Button x:Name="btnRun" Content="Run" HorizontalAlignment="Left" Margin="135,217,0,0" VerticalAlignment="Top" Width="75" Click="btnRun_Click" IsEnabled="False"/>
|
||||
<ListBox x:Name="lbDisassembly" ItemsSource="{Binding DisassembledOpcodes}" HorizontalAlignment="Left" Height="277" Margin="10,271,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="159" Margin="215,389,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="721"/>
|
||||
<Button x:Name="btnBreak" Content="Break" HorizontalAlignment="Left" Margin="135,244,0,0" VerticalAlignment="Top" Width="75" Click="btnBreak_Click"/>
|
||||
<TextBox x:Name="tbDebugEntry" HorizontalAlignment="Left" Height="23" Margin="215,637,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="721" KeyDown="tbDebugEntry_KeyDown"/>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
|
|
@ -29,6 +29,9 @@ namespace _6502EmulatorFrontend
|
|||
Thread M6502WorkerThread;
|
||||
MainWindowViewModel vm = new MainWindowViewModel();
|
||||
byte[] videoRom = File.ReadAllBytes("C:/apple/apple1.vid");
|
||||
string monitorRomPath;
|
||||
string basicRomPath;
|
||||
SettingsWindow settingsWindow;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
|
@ -55,21 +58,24 @@ namespace _6502EmulatorFrontend
|
|||
vm.Processor.ExecutionStopped += new M6502.ExecutionStoppedEventHandler(onExecutionStopped);
|
||||
TextCompositionManager.AddTextInputHandler(this, new TextCompositionEventHandler(OnTextComposition));
|
||||
|
||||
//Set up settings window
|
||||
settingsWindow = new SettingsWindow();
|
||||
settingsWindow.RomPathsSaved += new SettingsWindow.RomPathsSavedEventHandler(OnRomPathsSaved);
|
||||
settingsWindow.swvm.BasicRomPath = @"C:\apple\apple1basic.bin";
|
||||
settingsWindow.swvm.MonitorRomPath = @"C:\apple\apple1.rom";
|
||||
basicRomPath = @"C:\apple\apple1basic.bin";
|
||||
monitorRomPath = @"C:\apple\apple1.rom";
|
||||
|
||||
//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);
|
||||
Interop.loadBinary(monitorRomPath, 0xFF00);
|
||||
Interop.loadBinary(basicRomPath, 0xE000);
|
||||
decodeGraphics();
|
||||
|
||||
Interop.resetProcessor();
|
||||
|
@ -94,16 +100,6 @@ namespace _6502EmulatorFrontend
|
|||
|
||||
}
|
||||
|
||||
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++)
|
||||
|
@ -134,35 +130,7 @@ namespace _6502EmulatorFrontend
|
|||
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)
|
||||
{
|
||||
|
@ -224,5 +192,10 @@ namespace _6502EmulatorFrontend
|
|||
tbDebugEntry.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void MenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
settingsWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using _6502EmulatorFrontend.cpu;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace _6502EmulatorFrontend
|
||||
{
|
||||
partial class MainWindow
|
||||
{
|
||||
public class RomPathEventArgs : EventArgs
|
||||
{
|
||||
public readonly string MonitorPath;
|
||||
public readonly string BasicPath;
|
||||
|
||||
public RomPathEventArgs(string monitorPath, string basicPath)
|
||||
{
|
||||
MonitorPath = monitorPath;
|
||||
BasicPath = basicPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRomPathsSaved(object sender, RomPathEventArgs e)
|
||||
{
|
||||
monitorRomPath = e.MonitorPath;
|
||||
basicRomPath = e.BasicPath;
|
||||
}
|
||||
|
||||
private void OnTextComposition(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
Interop.putKeyInBuffer((byte)e.Text.ToUpper().ToCharArray()[0]);
|
||||
}
|
||||
|
||||
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 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"; }));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<Window x:Class="_6502EmulatorFrontend.SettingsWindow"
|
||||
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="SettingsWindow" Height="300" Width="525.627">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="22*"/>
|
||||
<RowDefinition Height="93*"/>
|
||||
<RowDefinition Height="20*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="65*"/>
|
||||
<ColumnDefinition Width="27*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" x:Name="label" Content="Monitor ROM ($FF00)" HorizontalAlignment="Left" Margin="10,8,0,0" VerticalAlignment="Top" Width="130" Height="26"/>
|
||||
<Label Grid.Column="0" x:Name="label1" Content="Basic ROM ($E000)" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="130" Grid.Row="1" Height="26"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" x:Name="tbMonitorRomPath" HorizontalAlignment="Left" Height="23" Margin="10,12,0,0" TextWrapping="Wrap" Text="{Binding Path=MonitorRomPath}" VerticalAlignment="Top" Width="240"/>
|
||||
<Button x:Name="btnMonitorRom" Content="Browse" Grid.Column="2" HorizontalAlignment="Left" Margin="10,12,0,0" VerticalAlignment="Top" Width="88" Height="22"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" x:Name="tbBasicRomPath" HorizontalAlignment="Left" Height="23" Margin="10,12,0,0" TextWrapping="Wrap" Text="{Binding Path=BasicRomPath}" VerticalAlignment="Top" Width="240"/>
|
||||
<Button x:Name="btnBasicRom" Content="Browse" Grid.Column="2" HorizontalAlignment="Left" Margin="10,12,0,0" VerticalAlignment="Top" Width="88" Height="22" Grid.Row="1"/>
|
||||
<Button x:Name="btnSave" Content="Save" Grid.Column="2" HorizontalAlignment="Left" Margin="10,8,0,0" Grid.Row="2" VerticalAlignment="Top" Width="88" Click="btnSave_Click"/>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace _6502EmulatorFrontend
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SettingsWindow.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsWindow : Window
|
||||
{
|
||||
public event RomPathsSavedEventHandler RomPathsSaved;
|
||||
public delegate void RomPathsSavedEventHandler(object sender, MainWindow.RomPathEventArgs e);
|
||||
|
||||
public SettingsWindowViewModel swvm;
|
||||
|
||||
public SettingsWindow()
|
||||
{
|
||||
swvm = new SettingsWindowViewModel();
|
||||
InitializeComponent();
|
||||
DataContext = swvm;
|
||||
}
|
||||
|
||||
private void btnSave_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace _6502EmulatorFrontend
|
||||
{
|
||||
public class SettingsWindowViewModel
|
||||
{
|
||||
private string _monitorRomPath;
|
||||
private string _basicRomPath;
|
||||
|
||||
public string MonitorRomPath
|
||||
{
|
||||
get { return _monitorRomPath; }
|
||||
set
|
||||
{
|
||||
if (value != _monitorRomPath)
|
||||
{
|
||||
_monitorRomPath = value;
|
||||
OnPropertyChanged("MonitorRomPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string BasicRomPath
|
||||
{
|
||||
get { return _basicRomPath; }
|
||||
set
|
||||
{
|
||||
if (value != _basicRomPath)
|
||||
{
|
||||
_basicRomPath = value;
|
||||
OnPropertyChanged("BasicRomPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
*/
|
||||
|
||||
//The master processor of instructions.
|
||||
bool CPU::lastInstructionCrossedPageBoundary = false;
|
||||
|
||||
void CPU::process_instruction() {
|
||||
last_executed_opcode = "";
|
||||
last_operand = 0x00;
|
||||
|
@ -198,7 +200,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "ORA (M),Y $";
|
||||
op_ora(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_ora(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -230,7 +232,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "ORA M,Y $";
|
||||
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -238,7 +240,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "ORA M,X $";
|
||||
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -350,7 +352,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "AND (M),Y $";
|
||||
op_and(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_and(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -382,7 +384,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "AND M,Y $";
|
||||
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -390,7 +392,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "AND M,X $";
|
||||
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -494,7 +496,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "EOR (ZP),Y $";
|
||||
op_eor(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_eor(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -526,7 +528,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "EOR M,Y $";
|
||||
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -534,7 +536,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "EOR M,X $";
|
||||
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -640,7 +642,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "ADC (ZP),Y $";
|
||||
op_adc(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_adc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -680,7 +682,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "ADC M,Y $";
|
||||
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -688,7 +690,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "ADC M,X $";
|
||||
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -944,7 +946,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "LDA (ZP),Y $";
|
||||
op_lda(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_lda(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -984,7 +986,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "LDA M,Y $";
|
||||
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1000,7 +1002,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "LDY M,X $";
|
||||
op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1008,7 +1010,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "LDA M,X $";
|
||||
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1016,7 +1018,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "LDX M,Y $";
|
||||
op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1120,7 +1122,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "CMP (ZP),Y";
|
||||
op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1152,7 +1154,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "CMP M,Y $";
|
||||
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1160,7 +1162,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "CMP M,X $";
|
||||
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1271,7 +1273,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 5;
|
||||
last_executed_opcode = "SBC (ZP),Y $";
|
||||
op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_Y));
|
||||
op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1303,7 +1305,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "SBC M,Y $";
|
||||
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
|
||||
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1311,7 +1313,7 @@ void CPU::execute_opcode(uint8_t opcode)
|
|||
{
|
||||
cycles += 4;
|
||||
last_executed_opcode = "SBC M,X $";
|
||||
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
|
||||
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,13 @@ public:
|
|||
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 };
|
||||
|
||||
static bool lastInstructionCrossedPageBoundary;
|
||||
|
||||
bool frameInstructionsComplete();
|
||||
void resetCycleCounter();
|
||||
|
||||
void reset_processor() {
|
||||
lastInstructionCrossedPageBoundary = false;
|
||||
FLAG_SIGN = false;
|
||||
FLAG_OVERFLOW = false;
|
||||
FLAG_BREAKPOINT = false;
|
||||
|
@ -117,13 +120,13 @@ public:
|
|||
void print_status(uint8_t opcode);
|
||||
|
||||
/* Opcodes */
|
||||
void CPU::op_adc(WideAddress address);
|
||||
void CPU::op_adc(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_adc(uint8_t immediate);
|
||||
void CPU::op_adc(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_and(WideAddress address);
|
||||
void CPU::op_and(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_and(uint8_t immediate);
|
||||
void CPU::op_and(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_asl(WideAddress address);
|
||||
|
||||
|
@ -157,9 +160,9 @@ public:
|
|||
|
||||
void CPU::op_clv();
|
||||
|
||||
void CPU::op_cmp(uint8_t immediate);
|
||||
void CPU::op_cmp(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_cmp(WideAddress address);
|
||||
void CPU::op_cmp(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_cpx(uint8_t immediate);
|
||||
|
||||
|
@ -175,9 +178,9 @@ public:
|
|||
|
||||
void CPU::op_dey();
|
||||
|
||||
void CPU::op_eor(uint8_t immediate);
|
||||
void CPU::op_eor(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_eor(WideAddress address);
|
||||
void CPU::op_eor(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_inc(WideAddress address);
|
||||
|
||||
|
@ -189,17 +192,17 @@ public:
|
|||
|
||||
void CPU::op_jsr(WideAddress address);
|
||||
|
||||
void CPU::op_lda(uint8_t immediate);
|
||||
void CPU::op_lda(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_lda(WideAddress address);
|
||||
void CPU::op_lda(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_ldx(uint8_t immediate);
|
||||
void CPU::op_ldx(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_ldx(WideAddress address);
|
||||
void CPU::op_ldx(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_ldy(uint8_t immediate);
|
||||
void CPU::op_ldy(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_ldy(WideAddress address);
|
||||
void CPU::op_ldy(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_lsr();
|
||||
|
||||
|
@ -207,9 +210,9 @@ public:
|
|||
|
||||
void CPU::op_nop();
|
||||
|
||||
void CPU::op_ora(uint8_t immediate);
|
||||
void CPU::op_ora(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_ora(WideAddress address);
|
||||
void CPU::op_ora(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_pha();
|
||||
|
||||
|
@ -231,9 +234,9 @@ public:
|
|||
|
||||
void CPU::op_rts();
|
||||
|
||||
void CPU::op_sbc(uint8_t immediate);
|
||||
void CPU::op_sbc(uint8_t immediate, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_sbc(WideAddress address);
|
||||
void CPU::op_sbc(WideAddress address, bool crossedPageBoundary = false);
|
||||
|
||||
void CPU::op_sec();
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <iostream>
|
||||
|
||||
uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
||||
lastInstructionCrossedPageBoundary = false;
|
||||
|
||||
switch (mode) {
|
||||
case ADDRESSING_IMMEDIATE:
|
||||
|
@ -24,6 +25,7 @@ uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
|||
case ADDRESSING_ZEROPAGE_X:
|
||||
{
|
||||
uint8_t address = fetch_operand(ADDRESSING_IMMEDIATE);
|
||||
lastInstructionCrossedPageBoundary = (address + index_x < address);
|
||||
uint8_t operand = fetch_zero_page_byte(address + index_x);
|
||||
last_operand = address;
|
||||
return operand;
|
||||
|
@ -32,6 +34,7 @@ uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
|||
case ADDRESSING_ZEROPAGE_Y:
|
||||
{
|
||||
uint8_t address = fetch_operand(ADDRESSING_IMMEDIATE);
|
||||
lastInstructionCrossedPageBoundary = (address + index_y < address);
|
||||
uint8_t operand = fetch_zero_page_byte(address + index_y);
|
||||
last_operand = address;
|
||||
return operand;
|
||||
|
@ -52,6 +55,7 @@ uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
|||
uint8_t addrLow = fetch_memory_byte(program_counter);
|
||||
uint8_t addrHigh = fetch_memory_byte(program_counter);
|
||||
WideAddress address = { addrHigh, addrLow };
|
||||
lastInstructionCrossedPageBoundary = (addrLow + index_x < addrLow);
|
||||
uint8_t operand = fetch_memory_byte(address.add(index_x, false), false);
|
||||
last_operand = address;
|
||||
return operand;
|
||||
|
@ -62,6 +66,7 @@ uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
|||
uint8_t addrLow = fetch_memory_byte(program_counter);
|
||||
uint8_t addrHigh = fetch_memory_byte(program_counter);
|
||||
WideAddress address = { addrHigh, addrLow };
|
||||
lastInstructionCrossedPageBoundary = (addrLow + index_y < addrLow);
|
||||
uint8_t operand = fetch_memory_byte(address.add(index_y, false), false);
|
||||
last_operand = address;
|
||||
return operand;
|
||||
|
@ -79,6 +84,7 @@ uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
|
|||
case ADDRESSING_INDIRECT_Y:
|
||||
{
|
||||
WideAddress destination = fetch_dereferenced_zero_page_pointer(INDEX_NONE);
|
||||
lastInstructionCrossedPageBoundary = ((destination & 0xFF00) + index_y) > (destination & 0xFF00);
|
||||
destination = destination.add(index_y, false);
|
||||
uint8_t operand = fetch_memory_byte(destination);
|
||||
last_operand = operand;
|
||||
|
|
|
@ -25,11 +25,16 @@ unsigned char hex2bcd(unsigned char x)
|
|||
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. */
|
||||
When branching because we don't know if we were successful until now
|
||||
so we need to add +1 to cycles on a successful branch and another +1 if the branch
|
||||
goes to a different page.
|
||||
|
||||
crossedPageBoundary tells us if we need to increase cycle count by 1 if we crossed the page boundary
|
||||
CPU::lastInstructionCrossedPageBoundary keeps track of if we did or not
|
||||
if both are true, increase cycle count by 1.
|
||||
*/
|
||||
|
||||
void CPU::op_adc(WideAddress address) {
|
||||
void CPU::op_adc(WideAddress address, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
t = A + M + P.C
|
||||
|
@ -44,6 +49,10 @@ void CPU::op_adc(WideAddress address) {
|
|||
A = t & 0xFF
|
||||
*/
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
if (accumulator == 0x7f) {
|
||||
int x = 0;
|
||||
}
|
||||
|
@ -64,7 +73,7 @@ void CPU::op_adc(WideAddress address) {
|
|||
|
||||
};
|
||||
|
||||
void CPU::op_adc(uint8_t immediate) {
|
||||
void CPU::op_adc(uint8_t immediate, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
t = A + M + P.C
|
||||
|
@ -79,6 +88,10 @@ void CPU::op_adc(uint8_t immediate) {
|
|||
A = t & 0xFF
|
||||
*/
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
if (accumulator == 0x7f) {
|
||||
int x = 0;
|
||||
}
|
||||
|
@ -108,23 +121,33 @@ void CPU::op_adc(uint8_t immediate) {
|
|||
accumulator = (t & 0xFF);
|
||||
};
|
||||
|
||||
void CPU::op_and(WideAddress address) {
|
||||
void CPU::op_and(WideAddress address, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
A = A & M
|
||||
P.N = A.7
|
||||
P.Z = (A==0) ? 1:0 */
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = (accumulator & fetch_memory_byte(address, false));
|
||||
FLAG_SIGN = (accumulator & 0x80) == 0x80;
|
||||
FLAG_ZERO = (accumulator == 0);
|
||||
}
|
||||
|
||||
void CPU::op_and(uint8_t immediate) {
|
||||
void CPU::op_and(uint8_t immediate, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
A = A & M
|
||||
P.N = A.7
|
||||
P.Z = (A==0) ? 1:0 */
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = (accumulator & immediate);
|
||||
FLAG_SIGN = (accumulator & 0x80) == 0x80;
|
||||
FLAG_ZERO = (accumulator == 0);
|
||||
|
@ -162,9 +185,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -173,9 +196,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -184,9 +207,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -210,16 +233,16 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::op_bne(int8_t relative) {
|
||||
if (!PZ) {
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
if (relative == (int8_t)0xFE) {
|
||||
std::exit(4);
|
||||
|
@ -231,9 +254,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -253,9 +276,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -264,9 +287,9 @@ 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++;
|
||||
cycles++;
|
||||
}
|
||||
EmulatorState::Instance()->processor.cycles++;
|
||||
cycles++;
|
||||
program_counter += relative;
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +310,7 @@ void CPU::op_clv() {
|
|||
PV = false;
|
||||
}
|
||||
|
||||
void CPU::op_cmp(uint8_t immediate) { //immediate
|
||||
void CPU::op_cmp(uint8_t immediate, bool crossedPageBoundary) { //immediate
|
||||
/*
|
||||
Logic:
|
||||
t = A - M
|
||||
|
@ -296,6 +319,10 @@ void CPU::op_cmp(uint8_t immediate) { //immediate
|
|||
P.Z = (t==0) ? 1:0
|
||||
*/
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
uint8_t t = accumulator - immediate;
|
||||
PN = (t & 0x80) == 0x80;
|
||||
PC = (accumulator >= immediate);
|
||||
|
@ -303,7 +330,7 @@ void CPU::op_cmp(uint8_t immediate) { //immediate
|
|||
|
||||
}
|
||||
|
||||
void CPU::op_cmp(WideAddress address) {
|
||||
void CPU::op_cmp(WideAddress address, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
t = A - M
|
||||
|
@ -312,6 +339,10 @@ void CPU::op_cmp(WideAddress address) {
|
|||
P.Z = (t==0) ? 1:0
|
||||
*/
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
uint8_t t = accumulator - fetch_memory_byte(address, false);
|
||||
PN = (t & 0x80) == 0x80;
|
||||
PC = (accumulator >= fetch_memory_byte(address, false));
|
||||
|
@ -372,13 +403,23 @@ void CPU::op_dey() {
|
|||
PN = (index_y & 0x80) == 0x80;
|
||||
}
|
||||
|
||||
void CPU::op_eor(uint8_t immediate) {
|
||||
void CPU::op_eor(uint8_t immediate, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = accumulator ^ immediate;
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
}
|
||||
|
||||
void CPU::op_eor(WideAddress address) {
|
||||
void CPU::op_eor(WideAddress address, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = accumulator ^ fetch_memory_byte(address, false);
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
|
@ -430,37 +471,67 @@ void CPU::op_jsr(WideAddress address) {
|
|||
program_counter = address;
|
||||
}
|
||||
|
||||
void CPU::op_lda(uint8_t immediate) {
|
||||
void CPU::op_lda(uint8_t immediate, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = immediate;
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
}
|
||||
|
||||
void CPU::op_lda(WideAddress address) {
|
||||
void CPU::op_lda(WideAddress address, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = fetch_memory_byte(address, false);
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
}
|
||||
|
||||
void CPU::op_ldx(uint8_t immediate) {
|
||||
void CPU::op_ldx(uint8_t immediate, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
index_x = immediate;
|
||||
PN = (index_x & 0x80) == 0x80;
|
||||
PZ = (index_x == 0);
|
||||
}
|
||||
|
||||
void CPU::op_ldx(WideAddress address) {
|
||||
void CPU::op_ldx(WideAddress address, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
index_x = fetch_memory_byte(address, false);
|
||||
PN = (index_x & 0x80) == 0x80;
|
||||
PZ = (index_x == 0);
|
||||
}
|
||||
|
||||
void CPU::op_ldy(uint8_t immediate) {
|
||||
void CPU::op_ldy(uint8_t immediate, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
index_y = immediate;
|
||||
PN = (index_y & 0x80) == 0x80;
|
||||
PZ = (index_y == 0);
|
||||
}
|
||||
|
||||
void CPU::op_ldy(WideAddress address) {
|
||||
void CPU::op_ldy(WideAddress address, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
index_y = fetch_memory_byte(address, false);
|
||||
PN = (index_y & 0x80) == 0x80;
|
||||
PZ = (index_y == 0);
|
||||
|
@ -498,13 +569,23 @@ void CPU::op_nop() {
|
|||
//NOP
|
||||
}
|
||||
|
||||
void CPU::op_ora(uint8_t immediate) {
|
||||
void CPU::op_ora(uint8_t immediate, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = accumulator | immediate;
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
}
|
||||
|
||||
void CPU::op_ora(WideAddress address) {
|
||||
void CPU::op_ora(WideAddress address, bool crossedPageBoundary) {
|
||||
|
||||
if (crossedPageBoundary && lastInstructionCrossedPageBoundary) {
|
||||
cycles++;
|
||||
}
|
||||
|
||||
accumulator = accumulator | fetch_memory_byte(address, false);
|
||||
PN = (accumulator & 0x80) == 0x80;
|
||||
PZ = (accumulator == 0);
|
||||
|
@ -613,7 +694,7 @@ void CPU::op_rts() {
|
|||
program_counter = addr.add(1, false);
|
||||
}
|
||||
|
||||
void CPU::op_sbc(uint8_t immediate) {
|
||||
void CPU::op_sbc(uint8_t immediate, bool crossedPageBoundary) {
|
||||
/*
|
||||
Logic:
|
||||
IF (P.D)
|
||||
|
@ -629,7 +710,7 @@ void CPU::op_sbc(uint8_t immediate) {
|
|||
*/
|
||||
//TODO: decimal
|
||||
|
||||
op_adc(~immediate);
|
||||
op_adc(~immediate, crossedPageBoundary);
|
||||
|
||||
/*
|
||||
int8_t t;
|
||||
|
@ -649,8 +730,8 @@ void CPU::op_sbc(uint8_t immediate) {
|
|||
*/
|
||||
}
|
||||
|
||||
void CPU::op_sbc(WideAddress address) {
|
||||
op_adc(~fetch_memory_byte(address, false));
|
||||
void CPU::op_sbc(WideAddress address, bool crossedPageBoundary) {
|
||||
op_adc(~fetch_memory_byte(address, false), crossedPageBoundary);
|
||||
|
||||
/*
|
||||
int8_t t;
|
||||
|
|
Loading…
Reference in New Issue