mirror of https://github.com/Washi1337/Emux.git
Started on breakpoint conditions, expression parser.
This commit is contained in:
parent
e9bddd1353
commit
2be31b675c
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
|
||||
namespace Emux.GameBoy.Cpu
|
||||
{
|
||||
public class Breakpoint
|
||||
{
|
||||
public static readonly Predicate<GameBoyCpu> BreakAlways = _ => true;
|
||||
|
||||
public Breakpoint(ushort offset)
|
||||
: this(offset, BreakAlways)
|
||||
{
|
||||
}
|
||||
|
||||
public Breakpoint(ushort offset, Predicate<GameBoyCpu> condition)
|
||||
{
|
||||
Offset = offset;
|
||||
Condition = condition;
|
||||
}
|
||||
|
||||
public ushort Offset
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Predicate<GameBoyCpu> Condition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Offset.ToString("X4");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,14 +38,18 @@ namespace Emux.GameBoy.Cpu
|
|||
private readonly GameBoy _device;
|
||||
private readonly ManualResetEvent _continueSignal = new ManualResetEvent(false);
|
||||
private readonly ManualResetEvent _terminateSignal = new ManualResetEvent(false);
|
||||
|
||||
private ulong _ticks;
|
||||
private bool _break = true;
|
||||
private bool _halt = false;
|
||||
|
||||
private readonly NativeTimer _frameTimer;
|
||||
private readonly ManualResetEvent _frameStartSignal = new ManualResetEvent(false);
|
||||
private readonly ManualResetEvent _breakSignal = new ManualResetEvent(false);
|
||||
private TimeSpan _frameStartTime;
|
||||
private ulong _frameStartTickCount;
|
||||
|
||||
private readonly IDictionary<ushort, Breakpoint> _breakpoints = new Dictionary<ushort, Breakpoint>();
|
||||
|
||||
public GameBoyCpu(GameBoy device)
|
||||
{
|
||||
|
@ -56,7 +60,6 @@ namespace Emux.GameBoy.Cpu
|
|||
|
||||
Registers = new RegisterBank();
|
||||
Alu = new GameBoyAlu(Registers);
|
||||
Breakpoints = new HashSet<ushort>();
|
||||
EnableFrameLimit = true;
|
||||
|
||||
new Thread(CpuLoop)
|
||||
|
@ -107,14 +110,6 @@ namespace Emux.GameBoy.Cpu
|
|||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of memory addresses to break the execution on.
|
||||
/// </summary>
|
||||
public ISet<ushort> Breakpoints
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the processor should limit the execution speed to the original GameBoy clock speed.
|
||||
/// Disable this if experiencing heavy performance losses.
|
||||
|
@ -197,7 +192,7 @@ namespace Emux.GameBoy.Cpu
|
|||
}
|
||||
}
|
||||
|
||||
if (Breakpoints.Contains(Registers.PC))
|
||||
if (_breakpoints.TryGetValue(Registers.PC, out var breakpoint) && breakpoint.Condition(this))
|
||||
_break = true;
|
||||
|
||||
} while (!_break);
|
||||
|
@ -283,6 +278,38 @@ namespace Emux.GameBoy.Cpu
|
|||
_continueSignal.Reset();
|
||||
_terminateSignal.Set();
|
||||
}
|
||||
|
||||
public Breakpoint SetBreakpoint(ushort address)
|
||||
{
|
||||
if (!_breakpoints.TryGetValue(address, out var breakpoint))
|
||||
{
|
||||
breakpoint = new Breakpoint(address);
|
||||
_breakpoints.Add(address, breakpoint);
|
||||
}
|
||||
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
public void RemoveBreakpoint(ushort address)
|
||||
{
|
||||
_breakpoints.Remove(address);
|
||||
}
|
||||
|
||||
public IEnumerable<Breakpoint> GetBreakpoints()
|
||||
{
|
||||
return _breakpoints.Values;
|
||||
}
|
||||
|
||||
public Breakpoint GetBreakpointAtAddress(ushort address)
|
||||
{
|
||||
_breakpoints.TryGetValue(address, out var breakpoint);
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
public void ClearBreakpoints()
|
||||
{
|
||||
_breakpoints.Clear();
|
||||
}
|
||||
|
||||
protected virtual void OnResumed()
|
||||
{
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
<Compile Include="Cartridge\StreamedExternalMemory.cs" />
|
||||
<Compile Include="Cheating\GamesharkCode.cs" />
|
||||
<Compile Include="Cheating\GamesharkController.cs" />
|
||||
<Compile Include="Cpu\Breakpoint.cs" />
|
||||
<Compile Include="Cpu\GameBoyAlu.cs" />
|
||||
<Compile Include="Cpu\InterruptFlags.cs" />
|
||||
<Compile Include="Cpu\RegisterFlags.cs" />
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System.Windows;
|
||||
using Emux.Expressions;
|
||||
using Emux.GameBoy.Cpu;
|
||||
using Emux.Properties;
|
||||
|
||||
namespace Emux
|
||||
|
|
|
@ -64,6 +64,10 @@
|
|||
</ApplicationDefinition>
|
||||
<Compile Include="DeviceEventArgs.cs" />
|
||||
<Compile Include="DeviceManager.cs" />
|
||||
<Compile Include="Expressions\ExpressionLexer.cs" />
|
||||
<Compile Include="Expressions\ExpressionParser.cs" />
|
||||
<Compile Include="Expressions\Terminal.cs" />
|
||||
<Compile Include="Expressions\Token.cs" />
|
||||
<Compile Include="Gui\AboutDialog.xaml.cs">
|
||||
<DependentUpon>AboutDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -71,6 +75,9 @@
|
|||
<Compile Include="Gui\AudioMixerWindow.xaml.cs">
|
||||
<DependentUpon>AudioMixerWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Gui\BreakpointDialog.xaml.cs">
|
||||
<DependentUpon>BreakpointDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Gui\CheatsWindow.xaml.cs">
|
||||
<DependentUpon>CheatsWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -108,6 +115,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Gui\BreakpointDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Gui\CheatsWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Emux.Expressions
|
||||
{
|
||||
public class ExpressionLexer
|
||||
{
|
||||
private static readonly ISet<string> Registers = new HashSet<string>
|
||||
{
|
||||
"PC", "SP", "AF", "BC", "DE", "HL", "A", "B", "C", "D", "E", "F", "H", "L",
|
||||
"pc", "sp", "af", "bc", "de", "hl", "a", "b", "c", "d", "e", "f", "h", "l"
|
||||
};
|
||||
|
||||
private const string HexCharacters = "0123456789ABCDEF";
|
||||
|
||||
private readonly TextReader _reader;
|
||||
private Token _bufferedToken;
|
||||
|
||||
public ExpressionLexer(TextReader reader)
|
||||
{
|
||||
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
||||
}
|
||||
|
||||
public bool HasNext()
|
||||
{
|
||||
SkipWhitespaces();
|
||||
return _reader.Peek() != -1;
|
||||
}
|
||||
|
||||
public Token Peek()
|
||||
{
|
||||
if (_bufferedToken == null && HasNext())
|
||||
ReadNextToken();
|
||||
|
||||
return _bufferedToken;
|
||||
}
|
||||
|
||||
public Token Next()
|
||||
{
|
||||
if (_bufferedToken == null)
|
||||
ReadNextToken();
|
||||
var token = _bufferedToken;
|
||||
_bufferedToken = null;
|
||||
return token;
|
||||
}
|
||||
|
||||
private void SkipWhitespaces()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int c = _reader.Peek();
|
||||
if (c == -1 || !char.IsWhiteSpace((char) c))
|
||||
break;
|
||||
_reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadNextToken()
|
||||
{
|
||||
if (!HasNext())
|
||||
throw new EndOfStreamException();
|
||||
|
||||
char c = (char) _reader.Peek();
|
||||
|
||||
_bufferedToken = char.IsLetterOrDigit(c) ? ReadNextWord() : ReadNextSymbol();
|
||||
}
|
||||
|
||||
private Token ReadNextWord()
|
||||
{
|
||||
string word = ReadWhile(char.IsLetterOrDigit);
|
||||
Terminal terminal;
|
||||
|
||||
if (Registers.Contains(word))
|
||||
terminal = Terminal.Register;
|
||||
else if (Regex.IsMatch(word, @"(0x[\da-zA-Z]+)|([\da-zA-Z]+h)"))
|
||||
terminal = Terminal.Hexadecimal;
|
||||
else if (Regex.IsMatch(word, @"\d+"))
|
||||
terminal = Terminal.Decimal;
|
||||
else
|
||||
throw new SyntaxErrorException("Unrecognized word '" + word + "'.");
|
||||
|
||||
return new Token(terminal, word);
|
||||
}
|
||||
|
||||
private string ReadWhile(Predicate<char> condition)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
while (true)
|
||||
{
|
||||
int p = _reader.Peek();
|
||||
if (p == -1)
|
||||
break;
|
||||
|
||||
char c = (char) p;
|
||||
if (!condition(c))
|
||||
break;
|
||||
|
||||
_reader.Read();
|
||||
builder.Append(c);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
|
||||
private Token ReadNextSymbol()
|
||||
{
|
||||
char c = (char) _reader.Read();
|
||||
switch (c)
|
||||
{
|
||||
case '(':
|
||||
return new Token(Terminal.LPar, "(");
|
||||
|
||||
case ')':
|
||||
return new Token(Terminal.RPar, ")");
|
||||
|
||||
case '!':
|
||||
return new Token(Terminal.Not, "!");
|
||||
|
||||
case '+':
|
||||
return new Token(Terminal.Plus, "+");
|
||||
|
||||
case '-':
|
||||
return new Token(Terminal.Minus, "-");
|
||||
|
||||
case '=':
|
||||
return new Token(Terminal.Equals, "=");
|
||||
|
||||
case '>':
|
||||
if (_reader.Peek() != '=')
|
||||
return new Token(Terminal.GreaterThan, "=");
|
||||
_reader.Read();
|
||||
return new Token(Terminal.GreaterThanOrEqual, ">=");
|
||||
|
||||
case '<':
|
||||
if (_reader.Peek() != '=')
|
||||
return new Token(Terminal.Equals, "=");
|
||||
_reader.Read();
|
||||
return new Token(Terminal.LessThanOrEqual, ">=");
|
||||
|
||||
case '&':
|
||||
if (_reader.Peek() != '&')
|
||||
return new Token(Terminal.BitwiseAnd, "&");
|
||||
_reader.Read();
|
||||
return new Token(Terminal.BooleanAnd, "&&");
|
||||
|
||||
case '|':
|
||||
if (_reader.Peek() != '|')
|
||||
return new Token(Terminal.BitwiseOr, "|");
|
||||
_reader.Read();
|
||||
return new Token(Terminal.BooleanOr, "||");
|
||||
|
||||
}
|
||||
|
||||
throw new SyntaxErrorException("Unrecognized character '" + c + "'.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using Emux.GameBoy.Cpu;
|
||||
|
||||
namespace Emux.Expressions
|
||||
{
|
||||
public class ExpressionParser
|
||||
{
|
||||
private static readonly Terminal[] OperatorPrecedence =
|
||||
{
|
||||
Terminal.GreaterThan,
|
||||
Terminal.GreaterThanOrEqual,
|
||||
Terminal.LessThan,
|
||||
Terminal.LessThanOrEqual,
|
||||
|
||||
Terminal.Equals,
|
||||
Terminal.NotEquals,
|
||||
|
||||
Terminal.BitwiseAnd,
|
||||
Terminal.BitwiseOr,
|
||||
|
||||
Terminal.LPar,
|
||||
Terminal.RPar
|
||||
};
|
||||
|
||||
private static readonly ParameterExpression CpuParameter = Expression.Parameter(typeof(GameBoyCpu));
|
||||
|
||||
public static Predicate<GameBoyCpu> CompileExpression(string code)
|
||||
{
|
||||
var lexer = new ExpressionLexer(new StringReader(code));
|
||||
var postFix = ToPostfix(lexer).ToArray();
|
||||
|
||||
var stack = new Stack<Expression>();
|
||||
foreach (var token in postFix)
|
||||
{
|
||||
switch (token.Terminal)
|
||||
{
|
||||
case Terminal.Register:
|
||||
stack.Push(Expression.PropertyOrField(
|
||||
Expression.Property(CpuParameter, "Registers"),
|
||||
token.Text.ToUpperInvariant()));
|
||||
break;
|
||||
|
||||
case Terminal.Hexadecimal:
|
||||
var matches = Regex.Matches(token.Text, @"[\da-fA-F]+");
|
||||
stack.Push(Expression.Constant(ushort.Parse(matches[matches.Count - 1].Value,
|
||||
NumberStyles.HexNumber)));
|
||||
break;
|
||||
|
||||
case Terminal.Decimal:
|
||||
stack.Push(Expression.Constant(ushort.Parse(token.Text)));
|
||||
break;
|
||||
|
||||
case Terminal.Equals:
|
||||
stack.Push(Expression.Equal(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.NotEquals:
|
||||
stack.Push(Expression.NotEqual(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.GreaterThan:
|
||||
// Mirrored operator to accomodate for stack order.
|
||||
stack.Push(Expression.LessThan(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.GreaterThanOrEqual:
|
||||
// Mirrored operator to accomodate for stack order.
|
||||
stack.Push(Expression.LessThanOrEqual(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.LessThan:
|
||||
// Mirrored operator to accomodate for stack order.
|
||||
stack.Push(Expression.GreaterThan(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.LessThanOrEqual:
|
||||
// Mirrored operator to accomodate for stack order.
|
||||
stack.Push(Expression.GreaterThanOrEqual(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.BitwiseAnd:
|
||||
stack.Push(Expression.And(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.BitwiseOr:
|
||||
stack.Push(Expression.Or(stack.Pop(), stack.Pop()));
|
||||
break;
|
||||
|
||||
case Terminal.BooleanAnd:
|
||||
var v2 = stack.Pop();
|
||||
var v1 = stack.Pop();
|
||||
stack.Push(Expression.AndAlso(v1, v2));
|
||||
break;
|
||||
|
||||
case Terminal.BooleanOr:
|
||||
v2 = stack.Pop();
|
||||
v1 = stack.Pop();
|
||||
stack.Push(Expression.OrElse(v1, v2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.Count >= 2)
|
||||
throw new SyntaxErrorException("Expression contains unused terms.");
|
||||
|
||||
var final = stack.Pop();
|
||||
var lambda = Expression.Lambda<Predicate<GameBoyCpu>>(final, CpuParameter);
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static IEnumerable<Token> ToPostfix(ExpressionLexer lexer)
|
||||
{
|
||||
// Shunting yard algorithm to transform the infix expression into postfix for easier interpretation.
|
||||
|
||||
var operatorStack = new Stack<Token>();
|
||||
while (lexer.HasNext())
|
||||
{
|
||||
var current = lexer.Next();
|
||||
switch (current.Terminal)
|
||||
{
|
||||
case Terminal.Decimal:
|
||||
case Terminal.Hexadecimal:
|
||||
case Terminal.Flag:
|
||||
case Terminal.Register:
|
||||
yield return current;
|
||||
break;
|
||||
|
||||
case Terminal.LPar:
|
||||
operatorStack.Push(current);
|
||||
break;
|
||||
|
||||
case Terminal.RPar:
|
||||
while (operatorStack.Peek().Terminal != Terminal.LPar)
|
||||
yield return operatorStack.Pop();
|
||||
operatorStack.Pop(); // Pop LPar
|
||||
break;
|
||||
|
||||
default:
|
||||
while (operatorStack.Count > 0)
|
||||
{
|
||||
var lastOperator = operatorStack.Peek();
|
||||
int lastPrecedence = Array.IndexOf(OperatorPrecedence, lastOperator.Terminal);
|
||||
int currentPrecedence = Array.IndexOf(OperatorPrecedence, current.Terminal);
|
||||
if (lastPrecedence <= currentPrecedence)
|
||||
{
|
||||
yield return operatorStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
operatorStack.Push(current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (operatorStack.Count > 0)
|
||||
yield return operatorStack.Pop();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
namespace Emux.Expressions
|
||||
{
|
||||
public enum Terminal
|
||||
{
|
||||
Not,
|
||||
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThan,
|
||||
GreaterThanOrEqual,
|
||||
|
||||
Plus,
|
||||
Minus,
|
||||
|
||||
BooleanAnd,
|
||||
BitwiseAnd,
|
||||
BooleanOr,
|
||||
BitwiseOr,
|
||||
|
||||
Register,
|
||||
Flag,
|
||||
|
||||
Decimal,
|
||||
Hexadecimal,
|
||||
RPar,
|
||||
LPar
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
namespace Emux.Expressions
|
||||
{
|
||||
public class Token
|
||||
{
|
||||
public Token(Terminal terminal, string text)
|
||||
{
|
||||
Terminal = terminal;
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public Terminal Terminal
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Text} ({Terminal})";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Window x:Class="Emux.Gui.BreakpointDialog"
|
||||
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:Emux"
|
||||
mc:Ignorable="d"
|
||||
Title="Breakpoint" Height="124.415" Width="336.585" WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Margin="5" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Margin = "5" Content="OK" Width="75" Click="OkButtonOnClick" IsDefault="True"/>
|
||||
<Button Margin = "5" Content="Cancel" Width="75" Click ="CancelButtonOnClick" IsCancel="True"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,25 @@
|
|||
using System.Windows;
|
||||
|
||||
namespace Emux.Gui
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for BreakpointDialog.xaml
|
||||
/// </summary>
|
||||
public partial class BreakpointDialog : Window
|
||||
{
|
||||
public BreakpointDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OkButtonOnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CancelButtonOnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ namespace Emux.Gui
|
|||
{
|
||||
public class InstructionItem : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private readonly GameBoy.GameBoy _gameBoy;
|
||||
private readonly Z80Instruction _instruction;
|
||||
|
||||
|
@ -24,17 +26,22 @@ namespace Emux.Gui
|
|||
|
||||
public bool IsBreakpoint
|
||||
{
|
||||
get { return _gameBoy.Cpu.Breakpoints.Contains(_instruction.Offset); }
|
||||
get { return _gameBoy.Cpu.GetBreakpointAtAddress(_instruction.Offset) != null; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
_gameBoy.Cpu.Breakpoints.Add(_instruction.Offset);
|
||||
_gameBoy.Cpu.SetBreakpoint(_instruction.Offset);
|
||||
else
|
||||
_gameBoy.Cpu.Breakpoints.Remove(_instruction.Offset);
|
||||
_gameBoy.Cpu.RemoveBreakpoint(_instruction.Offset);
|
||||
OnPropertyChanged(nameof(IsBreakpoint));
|
||||
}
|
||||
}
|
||||
|
||||
public Breakpoint Breakpoint
|
||||
{
|
||||
get { return _gameBoy.Cpu.GetBreakpointAtAddress(_instruction.Offset); }
|
||||
}
|
||||
|
||||
public bool IsCurrentInstruction
|
||||
{
|
||||
get { return _gameBoy.Cpu.Registers.PC == Offset; }
|
||||
|
@ -75,9 +82,6 @@ namespace Emux.Gui
|
|||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
|
|
|
@ -308,7 +308,7 @@ namespace Emux.Gui
|
|||
}
|
||||
else
|
||||
{
|
||||
_currentDevice.Cpu.Breakpoints.Add(address);
|
||||
_currentDevice.Cpu.SetBreakpoint(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ namespace Emux.Gui
|
|||
|
||||
private void ClearBreakpointsCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
_currentDevice.Cpu.Breakpoints.Clear();
|
||||
_currentDevice.Cpu.ClearBreakpoints();
|
||||
}
|
||||
|
||||
private void KeyPadCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
|
||||
|
|
Loading…
Reference in New Issue