old-SuperVM/SuperVM.VisualDebugger/VirtualMachineModel.cs

367 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using SuperVM.Assembler;
using System.Timers;
using System.Windows;
using Microsoft.Win32;
using System.IO;
namespace SuperVM.VisualDebugger
{
public class VirtualMachineModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Process process;
private VMAssembly assembly;
private Memory memory;
private readonly Timer timer;
private bool appendBootstrap = true;
private bool insertDebugNewlines = false;
public VirtualMachineModel()
{
this.memory = new Memory(1024);
this.memory.LoadString("Hallo Welt!", 33);
this.process = new Process()
{
Memory = this.memory,
};
this.process.SysCall += Process_SysCall;
this.Source = "\tcpget\n\tjmp @main\n\tsyscall [ci: 0]\n; Here your code!\nmain:";
this.StepCommand = new RelayCommand(this.Step);
this.ResetCommand = new RelayCommand(this.Reset);
this.RecompileCommand = new RelayCommand(this.Recompile);
this.StartCommand = new RelayCommand(this.Start);
this.StartSlowCommand = new RelayCommand(this.StartSlow);
this.StopCommand = new RelayCommand(this.Stop);
this.LoadFromClipboardCommand = new RelayCommand(this.LoadFromClipboard);
this.LoadFromFileCommand = new RelayCommand(this.LoadFromFile);
this.timer = new Timer();
this.timer.Elapsed += Timer_Elapsed;
this.Recompile();
this.Reset();
}
private void LoadFromFile()
{
var ofd = new OpenFileDialog()
{
Filter = "All Files (*.*)|*.*",
Title = "Load Assembler Source...",
AddExtension = false,
};
if (ofd.ShowDialog(App.Current.MainWindow) != true)
return;
var source = File.ReadAllText(ofd.FileName);
LoadSource(source);
}
private void LoadFromClipboard()
{
if (Clipboard.ContainsText(TextDataFormat.UnicodeText) == false)
return;
this.LoadSource(Clipboard.GetText(TextDataFormat.UnicodeText));
}
private void LoadSource(string source)
{
this.Source = source;
if (this.appendBootstrap)
{
this.Source = "\tcpget\n\tjmp @main\n\tsyscall [ci: 0]\n" + this.Source;
}
OnPropertyChanged(nameof(Source));
this.Recompile();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
this.Step();
}
private void Stop()
{
this.timer.Stop();
}
private void Start()
{
this.timer.Interval = 10.0;
this.timer.Start();
}
private void StartSlow()
{
this.timer.Interval = 100.0;
this.timer.Start();
}
private void Process_SysCall(object sender, Process.CommandExecutionEnvironment e)
{
var appendix = "";
if (this.insertDebugNewlines)
appendix = "\n";
switch (e.Additional)
{
case 0: // Stop execution
{
this.Reset();
break;
}
case 1: // Print char
{
this.Output += Encoding.ASCII.GetString(new[] { (byte)e.Input0 }) + appendix;
this.OnPropertyChanged(nameof(Output));
break;
}
case 2: // Print Numeric
{
this.Output += e.Input0.ToString() + appendix;
this.OnPropertyChanged(nameof(Output));
break;
}
}
}
public void Recompile()
{
this.Stop();
try
{
this.assembly = Assembler.Assembler.Assemble(this.Source);
this.process.Module = assembly.CreateModule();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "SuperVM Assembler", MessageBoxButton.OK, MessageBoxImage.Warning);
}
this.Reset();
}
public void Reset()
{
this.Stop();
this.process.Reset();
this.OnVmChanged();
}
public void Step()
{
try
{
this.process.Step();
}
catch (Exception ex)
{
this.Stop();
MessageBox.Show(ex.Message, "SuperVM", MessageBoxButton.OK, MessageBoxImage.Warning);
}
this.OnVmChanged();
}
private void OnVmChanged()
{
OnPropertyChanged(nameof(StackPointer));
OnPropertyChanged(nameof(BasePointer));
OnPropertyChanged(nameof(CodePointer));
OnPropertyChanged(nameof(FlagZ));
OnPropertyChanged(nameof(FlagN));
OnPropertyChanged(nameof(Stack));
OnPropertyChanged(nameof(CurrentSourceLine));
}
private void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int CurrentSourceLine => (this.CodePointer < this.assembly.OriginalLine.Count) ? (this.assembly.OriginalLine[this.CodePointer] - 1) : -1;
public int StackPointer
{
get { return (int)process.StackPointer; }
set { process.StackPointer = (uint)value; }
}
public int BasePointer
{
get { return (int)process.BasePointer; }
set { process.BasePointer = (uint)value; }
}
public int CodePointer
{
get { return (int)process.CodePointer; }
set { process.CodePointer = (uint)value; }
}
public bool FlagZ
{
get { return process.ZFlag; }
set { process.ZFlag = value; }
}
public bool FlagN
{
get { return process.NFlag; }
set { process.NFlag = value; }
}
public StackItem[] Stack => Enumerable
.Range(1, (int)this.process.StackPointer)
.Select(i => new StackItem(i, this.process))
.ToArray();
public MemoryProxy[] Memory => Enumerable
.Range(0, this.memory.Size / 8)
.Select(i => new MemoryProxy(this.memory, (uint)(8 * i)))
.ToArray();
public string Source { get; set; }
public string Output { get; set; }
public bool AppendBootstrap
{
get { return this.appendBootstrap; }
set
{
this.appendBootstrap = value;
OnPropertyChanged(nameof(AppendBootstrap));
}
}
public bool InsertDebugNewlines
{
get { return this.insertDebugNewlines; }
set
{
this.insertDebugNewlines = value;
OnPropertyChanged(nameof(InsertDebugNewlines));
}
}
public ICommand StepCommand { get; private set; }
public ICommand ResetCommand { get; private set; }
public ICommand RecompileCommand { get; private set; }
public ICommand StartCommand { get; private set; }
public ICommand StopCommand { get; private set; }
public ICommand StartSlowCommand { get; private set; }
public ICommand LoadFromClipboardCommand { get; private set; }
public ICommand LoadFromFileCommand { get; private set; }
}
public class StackItem
{
private readonly Process process;
public StackItem(int index, Process process)
{
this.Index = index;
this.process = process;
}
public int Index { get; private set; }
public int Value
{
get { return (int)this.process.Stack[this.Index]; }
set { this.process.Stack[this.Index] = (uint)value; }
}
}
public class MemoryProxy : INotifyPropertyChanged
{
private readonly Memory mem;
private readonly uint offset;
public event PropertyChangedEventHandler PropertyChanged;
public MemoryProxy(Memory mem, uint offset)
{
this.mem = mem;
this.offset = offset;
this.Offset = Convert.ToString(this.offset, 16);
this.mem.Changed += Mem_Changed;
}
private void Mem_Changed(object sender, MemoryChangedEventArgs e)
{
var upper = this.offset + 7;
if (e.Address >= this.offset && (e.Address + e.Length) <= upper)
{
var idx = (e.Address - offset) % 8;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("D" + idx));
}
}
private static string Read(byte val) => Convert.ToString(val, 16).ToUpper().PadLeft(2, '0');
private static byte Write(string val) => Convert.ToByte(val, 16);
public string Offset { get; private set; }
public string D0
{
get { return Read(this.mem[this.offset + 0]); }
set { this.mem[this.offset + 0] = Write(value); }
}
public string D1
{
get { return Read(this.mem[this.offset + 1]); }
set { this.mem[this.offset + 1] = Write(value); }
}
public string D2
{
get { return Read(this.mem[this.offset + 2]); }
set { this.mem[this.offset + 2] = Write(value); }
}
public string D3
{
get { return Read(this.mem[this.offset + 3]); }
set { this.mem[this.offset + 3] = Write(value); }
}
public string D4
{
get { return Read(this.mem[this.offset + 4]); }
set { this.mem[this.offset + 4] = Write(value); }
}
public string D5
{
get { return Read(this.mem[this.offset + 5]); }
set { this.mem[this.offset + 5] = Write(value); }
}
public string D6
{
get { return Read(this.mem[this.offset + 6]); }
set { this.mem[this.offset + 6] = Write(value); }
}
public string D7
{
get { return Read(this.mem[this.offset + 7]); }
set { this.mem[this.offset + 7] = Write(value); }
}
}
}