commit 5bcf363b6b3a597464e5744c94f43166a61f370c Author: Felix Queißner Date: Sat May 28 00:17:45 2016 +0200 Initial release: Port of the old SuperVM assembler, additional visual debugger. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b06e864 --- /dev/null +++ b/.gitignore @@ -0,0 +1,212 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +## TODO: Comment the next line if you want to checkin your +## web deploy settings but do note that will include unencrypted +## passwords +#*.pubxml + +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml diff --git a/SuperVM.Assembler/App.config b/SuperVM.Assembler/App.config new file mode 100644 index 0000000..8324aa6 --- /dev/null +++ b/SuperVM.Assembler/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SuperVM.Assembler/Assembler.cs b/SuperVM.Assembler/Assembler.cs new file mode 100644 index 0000000..a255df1 --- /dev/null +++ b/SuperVM.Assembler/Assembler.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SuperVM.Assembler +{ + public static class Assembler + { + static Regex annotationMatcher = new Regex(@"\[\s*(.*?)\s*\]", RegexOptions.Compiled); + static Regex labelMatcher = new Regex(@"^(\w+):\s*(.*)\s*$", RegexOptions.Compiled); + static Regex instructionMatcher = new Regex(@"(\w+)(?:\s+([@-]?\w+|'.'))?", RegexOptions.Compiled); + + public static VMAssembly Assemble(string src) + { + var sourceLines = src.Split(new[] { '\n' }); + var patches = new Dictionary(); + var labels = new Dictionary(); + + var code = new List(); + var instructions = new List(); + var lines = new List(); + var source = new List(); + for (int i = 0; i < sourceLines.Length; i++) + { + var line = sourceLines[i].Trim(); + { // Process comments + var idx = line.IndexOf(';'); + if (idx >= 0) + line = line.Substring(0, idx); + } + + var uncommented = line; + + { // Process labels + var match = labelMatcher.Match(line); + if (match.Success) + { + var label = match.Groups[1].Value; + labels.Add(label, code.Count); + line = match.Groups[2].Value; + } + } + + if (string.IsNullOrWhiteSpace(line)) + continue; + + var annotations = new HashSet(); + line = annotationMatcher.Replace(line, (m) => + { + annotations.Add(m.Groups[1].Value.ToLower()); + return ""; + }); + line = line.Trim(); + + { + var match = instructionMatcher.Match(line); + if (match.Success == false) + throw new InvalidOperationException("Invalid instruction: " + line); + + var mnemonic = match.Groups[1].Value; + + uint argument = 0; + if (match.Groups[2].Length > 0) + { + var argstring = match.Groups[2].Value; + if (argstring.StartsWith("@")) + { + // Add patch note for labels. + patches.Add(code.Count, argstring.Substring(1)); + } + else if (argstring.StartsWith("'")) + { + argument = (uint)argstring[1]; + } + else if (argstring.StartsWith("0x")) + { + argument = Convert.ToUInt32(argstring.Substring(2), 16); + } + else if (argstring.StartsWith("0b")) + { + argument = Convert.ToUInt32(argstring.Substring(2), 10); + } + else if (argstring.StartsWith("0d")) + { + argument = Convert.ToUInt32(argstring.Substring(2), 10); + } + else + { + if (argstring.StartsWith("-")) + { + unchecked + { + argument = (uint)Convert.ToInt32(argstring, 10); ; + } + } + else + argument = Convert.ToUInt32(argstring, 10); + } + } + + if (mnemonics.ContainsKey(mnemonic) == false) + { + throw new InvalidOperationException("Unknown mnemonic: " + mnemonic); + } + + var instruction = mnemonics[mnemonic]; + + foreach (var annotation in annotations) + { + if (annotation.StartsWith("ci:")) + { + instruction.CommandInfo = UInt16.Parse(annotation.Substring(3)); + continue; + } + + if (annotation.StartsWith("cmd:")) + { + instruction.Command = (Command)Enum.Parse(typeof(Command), annotation.Substring(4)); + continue; + } + + switch (annotation) + { + case "f:yes": + instruction.ModifyFlags = true; + break; + case "f:no": + instruction.ModifyFlags = false; + break; + case "r:discard": + instruction.Output = OutputType.Discard; + break; + case "r:push": + instruction.Output = OutputType.Push; + break; + case "r:jump": + instruction.Output = OutputType.Jump; + break; + case "i0:zero": + instruction.Input0 = InputType.Zero; + break; + case "i0:pop": + instruction.Input0 = InputType.Pop; + break; + case "i0:peek": + instruction.Input0 = InputType.Peek; + break; + case "i0:arg": + instruction.Input0 = InputType.Argument; + break; + case "i1:zero": + instruction.Input1 = InputType.Zero; + break; + case "i1:pop": + instruction.Input1 = InputType.Pop; + break; + case "ex(z)=x": + instruction.ExecutionZ = ExecutionMode.Always; + break; + case "ex(z)=0": + instruction.ExecutionZ = ExecutionMode.Zero; + break; + case "ex(z)=1": + instruction.ExecutionZ = ExecutionMode.One; + break; + case "ex(n)=x": + instruction.ExecutionN = ExecutionMode.Always; + break; + case "ex(n)=0": + instruction.ExecutionN = ExecutionMode.Zero; + break; + case "ex(n)=1": + instruction.ExecutionN = ExecutionMode.One; + break; + default: + throw new InvalidOperationException("Unrecognized annotation: " + annotation); + } + } + + ulong encoded = 0; + + encoded |= ((uint)(instruction.ExecutionZ) << 0); + encoded |= ((uint)(instruction.ExecutionN) << 2); + encoded |= ((uint)(instruction.Input0) << 4); + encoded |= ((uint)(instruction.Input1) << 6); + encoded |= ((uint)(instruction.Command) << 7); + encoded |= ((uint)(instruction.CommandInfo) << 13); + encoded |= ((uint)(instruction.ModifyFlags ? 1 : 0) << 29); + encoded |= ((uint)(instruction.Output) << 30); + encoded |= ((ulong)argument << 32); + + code.Add(encoded); + instructions.Add(instruction); + lines.Add(i + 1); + source.Add(uncommented); + } + } + + { // Install patches + foreach (var patch in patches) + { + var target = patch.Value; + var position = labels[target]; + code[patch.Key] = + (code[patch.Key] & 0xFFFFFFFF) | + ((ulong)position << 32); + } + } + + return new VMAssembly(code.ToArray(), instructions.ToArray(), lines.ToArray(), source.ToArray()); + } + + + static Dictionary mnemonics = new Dictionary() + { + { "nop", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "push", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "drop", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "dup", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Peek, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "jmp", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } }, + { "jmpi", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } }, + { "ret", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } }, + { "load", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Load, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "loadi", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Load, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "store", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Pop, Command = Command.Store, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "storei", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Store, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "get", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Get, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "geti", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Get, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "set", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Pop, Command = Command.Set, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "seti", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Set, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "bpget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.BpGet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "bpset", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.BpSet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "spget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.SpGet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "spset", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.SpSet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "cpget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.CpGet, CommandInfo = 1, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "add", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "sub", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 1, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "cmp", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 1, ModifyFlags = true, Output = OutputType.Discard, Argument = 0, } }, + { "mul", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 2, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "div", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 3, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "mod", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 4, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "and", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 5, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "or", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 6, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "xor", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 7, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "not", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Math, CommandInfo = 8, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "rol", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 9, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "ror", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 10, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "asl", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 11, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "asr", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 12, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "shl", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 13, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "shr", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 14, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } }, + { "syscall", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.SysCall, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + { "hwio", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.HwIO, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } }, + }; + } +} diff --git a/SuperVM.Assembler/MnemonicParser.cs b/SuperVM.Assembler/MnemonicParser.cs new file mode 100644 index 0000000..bc90c04 --- /dev/null +++ b/SuperVM.Assembler/MnemonicParser.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using System.Linq; + +namespace SuperVM.Assembler +{ + internal class MnemonicParser + { + public static void GenerateFromDocumentation(string file) + { + var relevantLines = File + .ReadAllLines(file) + .SkipWhile(l => l != "## Assembler Mnemonics") + .Skip(1) + .SkipWhile(l => string.IsNullOrWhiteSpace(l)) + .TakeWhile(l => l.StartsWith("|")) + .Skip(2) + .ToArray(); + + var instructions = relevantLines + .Select(l => l + .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .ToArray()) + .ToDictionary(a => a[0], a => new Instruction() + { + ExecutionN = ExecutionMode.Always, + ExecutionZ = ExecutionMode.Always, + Input0 = ToInputMode(a[2]), + Input1 = ToInputMode(a[3]), + Command = ToCommand(a[4]), + CommandInfo = ushort.Parse(a[5]), + ModifyFlags = (a[7].ToLower() == "yes"), + Output = ToOutput(a[6]), + Argument = 0, + }); + + var fields = typeof(Instruction).GetFields(); + foreach (var item in instructions) + { + // { "a", new Instruction() { } }, + Console.Write("{{ \"{0}\", new Instruction() {{ ", item.Key); + + foreach (var field in fields) + { + var value = field.GetValue(item.Value); + if (field.FieldType.IsEnum) + Console.Write("{0} = {1}.{2}, ", field.Name, field.FieldType.Name, value); + else + Console.Write("{0} = {1}, ", field.Name, value.ToString().ToLower()); + } + + Console.WriteLine(" } },"); + } + } + + private static OutputType ToOutput(string v) + { + switch (v.ToLower()) + { + case "discard": return OutputType.Discard; + case "push": return OutputType.Push; + case "jump": return OutputType.Jump; + default: + throw new NotSupportedException(); + } + } + + private static Command ToCommand(string v) + { + return (Command)Enum.Parse(typeof(Command), v, true); + } + + private static InputType ToInputMode(string v) + { + switch (v.ToLower()) + { + case "zero": return InputType.Zero; + case "peek": return InputType.Peek; + case "pop": return InputType.Pop; + case "arg": return InputType.Argument; + default: + throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/SuperVM.Assembler/Program.cs b/SuperVM.Assembler/Program.cs new file mode 100644 index 0000000..637338f --- /dev/null +++ b/SuperVM.Assembler/Program.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SuperVM.Assembler +{ + class Program + { + static void Main(string[] args) + { + if (args.Contains("-gen-code")) + { + MnemonicParser.GenerateFromDocumentation( + @"../supervm/supervm.md"); + return; + } + + foreach (var file in args.Where(a => !a.StartsWith("-") && Path.GetExtension(a) == ".asm")) + { + var output = Path.ChangeExtension(file, ".bin"); + var assembly = Assembler.Assemble(File.ReadAllText(file)); + + var code = assembly.Code; + + Console.WriteLine("{0}*{1}:", output, code.Count); + for (int i = 0; i < code.Count; i++) + { + Console.Write("; {0:D3} ", i); + PrintInstruction(code[i], assembly.Annotation[i]); + } + using (var fs = File.Open(output, FileMode.Create, FileAccess.Write)) + { + for (int i = 0; i < code.Count; i++) + { + var bits = BitConverter.GetBytes(code[i]); + if (BitConverter.IsLittleEndian == false) + { + bits = bits.Reverse().ToArray(); + } + fs.Write(bits, 0, bits.Length); + } + } + } + } + + static void PrintInstruction(ulong instr, string comment) + { + var str = Convert.ToString((long)instr, 2).PadLeft(64, '0'); + + var portions = new[] + { + new { Start = 0, Length = 32, Color = ConsoleColor.Red }, + new { Start = 32, Length = 2, Color = ConsoleColor.DarkGreen }, + new { Start = 34, Length = 1, Color = ConsoleColor.Green }, + new { Start = 35, Length = 16, Color = ConsoleColor.Magenta }, + new { Start = 51, Length = 6, Color = ConsoleColor.Yellow }, + new { Start = 57, Length = 1, Color = ConsoleColor.DarkCyan }, + new { Start = 58, Length = 2, Color = ConsoleColor.Cyan }, + new { Start = 60, Length = 2, Color = ConsoleColor.DarkBlue }, + new { Start = 62, Length = 2, Color = ConsoleColor.Blue }, + }; + + var fg = Console.ForegroundColor; + foreach (var portion in portions) + { + Console.ForegroundColor = portion.Color; + Console.Write("{0} ", str.Substring(portion.Start, portion.Length)); + } + Console.ForegroundColor = fg; + Console.WriteLine(" {0}", comment); + } + } +} diff --git a/SuperVM.Assembler/Properties/AssemblyInfo.cs b/SuperVM.Assembler/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a1cfb43 --- /dev/null +++ b/SuperVM.Assembler/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("SuperVM.Assembler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SuperVM.Assembler")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("a84b2773-0eb8-4f3a-b342-68ddd9bd675e")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SuperVM.Assembler/SuperVM.Assembler.csproj b/SuperVM.Assembler/SuperVM.Assembler.csproj new file mode 100644 index 0000000..a69f90f --- /dev/null +++ b/SuperVM.Assembler/SuperVM.Assembler.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {A84B2773-0EB8-4F3A-B342-68DDD9BD675E} + Exe + Properties + SuperVM.Assembler + SuperVM.Assembler + v4.6 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + {014295b7-06bd-47c7-b8ca-d046984b0f35} + SuperVM + + + + + \ No newline at end of file diff --git a/SuperVM.Assembler/VMAssembly.cs b/SuperVM.Assembler/VMAssembly.cs new file mode 100644 index 0000000..94bd833 --- /dev/null +++ b/SuperVM.Assembler/VMAssembly.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace SuperVM.Assembler +{ + public sealed class VMAssembly + { + private readonly ulong[] code; + private readonly Instruction[] instructions; + private readonly int[] lines; + private readonly string[] origins; + + public VMAssembly(ulong[] code, Instruction[] instructions, int[] lines, string[] origins) + { + this.code = code; + this.instructions = instructions; + this.lines = lines; + this.origins = origins; + } + + public Module CreateModule() + { + return new Module(this.instructions); + } + + public IReadOnlyList Code { get { return this.code; } } + public IReadOnlyList Instrucions { get { return this.instructions; } } + public IReadOnlyList OriginalLine { get { return this.lines; } } + public IReadOnlyList Annotation { get { return this.origins; } } + } +} \ No newline at end of file diff --git a/SuperVM.VisualDebugger/App.xaml b/SuperVM.VisualDebugger/App.xaml new file mode 100644 index 0000000..515f91c --- /dev/null +++ b/SuperVM.VisualDebugger/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/SuperVM.VisualDebugger/App.xaml.cs b/SuperVM.VisualDebugger/App.xaml.cs new file mode 100644 index 0000000..a3788fd --- /dev/null +++ b/SuperVM.VisualDebugger/App.xaml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace SuperVM.VisualDebugger +{ + /// + /// Interaktionslogik für "App.xaml" + /// + public partial class App : Application + { + } +} diff --git a/SuperVM.VisualDebugger/CodeEditor.cs b/SuperVM.VisualDebugger/CodeEditor.cs new file mode 100644 index 0000000..5395ba7 --- /dev/null +++ b/SuperVM.VisualDebugger/CodeEditor.cs @@ -0,0 +1,56 @@ +using ICSharpCode.AvalonEdit; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace SuperVM.VisualDebugger +{ + public sealed class CodeEditor : TextEditor, INotifyPropertyChanged + { + public static readonly DependencyProperty CodeProperty = + DependencyProperty.Register(nameof(Code), typeof(string), typeof(CodeEditor), + new PropertyMetadata((obj, args) => + { + var target = (CodeEditor)obj; + target.Dispatcher.BeginInvoke(new Action(() => + { + target.preventUpdate = true; + var cursor = target.CaretOffset; + target.Text = (string)args.NewValue; + target.CaretOffset = cursor; + target.preventUpdate = false; + })); + }) + ); + + private bool preventUpdate = false; + + public string Code + { + get { return (string)GetValue(CodeProperty); } + set + { + SetValue(CodeProperty, value); + base.Text = value; + } + } + + protected override void OnTextChanged(EventArgs e) + { + if(this.preventUpdate == false) + SetValue(CodeProperty, this.Text); + base.OnTextChanged(e); + } + + public event PropertyChangedEventHandler PropertyChanged; + public void RaisePropertyChanged(string info) + { + if (PropertyChanged != null) + PropertyChanged(this, new PropertyChangedEventArgs(info)); + } + } +} diff --git a/SuperVM.VisualDebugger/MainWindow.xaml b/SuperVM.VisualDebugger/MainWindow.xaml new file mode 100644 index 0000000..326a29c --- /dev/null +++ b/SuperVM.VisualDebugger/MainWindow.xaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SuperVM.VisualDebugger/MainWindow.xaml.cs b/SuperVM.VisualDebugger/MainWindow.xaml.cs new file mode 100644 index 0000000..0f34f40 --- /dev/null +++ b/SuperVM.VisualDebugger/MainWindow.xaml.cs @@ -0,0 +1,106 @@ +using ICSharpCode.AvalonEdit.Rendering; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +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.Navigation; +using System.Windows.Shapes; + +namespace SuperVM.VisualDebugger +{ + /// + /// Interaktionslogik für MainWindow.xaml + /// + public partial class MainWindow : Window + { + public static DependencyProperty CurrentLineProperty = DependencyProperty.Register( + nameof(CurrentLine), + typeof(int), + typeof(MainWindow), + new PropertyMetadata(-1, CurrentLineChanged)); + private readonly LineBackgroundRenderer renderer; + + private static void CurrentLineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = (MainWindow)d; + window.codeEditor.TextArea.TextView.InvalidateVisual(); + } + + public int CurrentLine + { + get { return (int)GetValue(CurrentLineProperty); } + set { SetValue(CurrentLineProperty, value); } + } + + public MainWindow() + { + InitializeComponent(); + + BindingOperations.SetBinding(this, CurrentLineProperty, new Binding("CurrentSourceLine")); + + this.codeEditor.TextArea.TextView.BackgroundRenderers.Add(this.renderer = new LineBackgroundRenderer(this)); + } + + private void MainWindow_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + + } + + public class LineBackgroundRenderer : IBackgroundRenderer + { + static Pen pen; + static SolidColorBrush background; + + private readonly MainWindow window; + + static LineBackgroundRenderer() + { + background = new SolidColorBrush(Color.FromRgb(0xFF, 0xFF, 0x00)); background.Freeze(); + + var blackBrush = new SolidColorBrush(Color.FromRgb(0, 0, 0)); blackBrush.Freeze(); + pen = new Pen(blackBrush, 0.0); + } + + public LineBackgroundRenderer(MainWindow window) + { + this.window = window; + } + + public KnownLayer Layer + { + get { return KnownLayer.Background; } + } + + public void Draw(TextView textView, DrawingContext drawingContext) + { + var currentLine = (this.window.DataContext as VirtualMachineModel)?.CurrentSourceLine ?? -1; + if (currentLine < 0) + return; + + foreach (var v in textView.VisualLines) + { + var rc = BackgroundGeometryBuilder.GetRectsFromVisualSegment(textView, v, 0, 1000).First(); + // NB: This lookup to fetch the doc line number isn't great, we could + // probably do it once then just increment. + var linenum = v.FirstDocumentLine.LineNumber - 1; + + if (linenum != currentLine) + continue; + + var brush = background; + + drawingContext.DrawRectangle( + brush, pen, + new Rect(0, rc.Top, textView.ActualWidth, rc.Height)); + } + } + } + } +} diff --git a/SuperVM.VisualDebugger/Properties/AssemblyInfo.cs b/SuperVM.VisualDebugger/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bffde45 --- /dev/null +++ b/SuperVM.VisualDebugger/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("SuperVM.VisualDebugger")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SuperVM.VisualDebugger")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie +//ImCodeVerwendeteKultur in der .csproj-Datei +//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch +//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung +//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile, +//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher + //(wird verwendet, wenn eine Ressource auf der Seite + // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.) + ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs + //(wird verwendet, wenn eine Ressource auf der Seite, in der Anwendung oder einem + // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.) +)] + + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SuperVM.VisualDebugger/Properties/Resources.Designer.cs b/SuperVM.VisualDebugger/Properties/Resources.Designer.cs new file mode 100644 index 0000000..0eeaa98 --- /dev/null +++ b/SuperVM.VisualDebugger/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace SuperVM.VisualDebugger.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [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() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SuperVM.VisualDebugger.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/SuperVM.VisualDebugger/Properties/Resources.resx b/SuperVM.VisualDebugger/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/SuperVM.VisualDebugger/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SuperVM.VisualDebugger/Properties/Settings.Designer.cs b/SuperVM.VisualDebugger/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5bdf298 --- /dev/null +++ b/SuperVM.VisualDebugger/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace SuperVM.VisualDebugger.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.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; + } + } + } +} diff --git a/SuperVM.VisualDebugger/Properties/Settings.settings b/SuperVM.VisualDebugger/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/SuperVM.VisualDebugger/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/SuperVM.VisualDebugger/RelayCommand.cs b/SuperVM.VisualDebugger/RelayCommand.cs new file mode 100644 index 0000000..fe0f971 --- /dev/null +++ b/SuperVM.VisualDebugger/RelayCommand.cs @@ -0,0 +1,21 @@ +using System; +using System.Windows.Input; + +namespace SuperVM.VisualDebugger +{ + internal class RelayCommand : ICommand + { + private Action action; + + public RelayCommand(Action action) + { + this.action = action; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter)=> this.action(); + } +} \ No newline at end of file diff --git a/SuperVM.VisualDebugger/SuperVM.VisualDebugger.csproj b/SuperVM.VisualDebugger/SuperVM.VisualDebugger.csproj new file mode 100644 index 0000000..29e5e2e --- /dev/null +++ b/SuperVM.VisualDebugger/SuperVM.VisualDebugger.csproj @@ -0,0 +1,120 @@ + + + + + Debug + AnyCPU + {4947C531-73DC-4533-A960-E5970B0A7CCE} + WinExe + Properties + SuperVM.VisualDebugger + SuperVM.VisualDebugger + v4.6.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\packages\AvalonEdit.5.0.2\lib\Net40\ICSharpCode.AvalonEdit.dll + True + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {a84b2773-0eb8-4f3a-b342-68ddd9bd675e} + SuperVM.Assembler + + + {014295b7-06bd-47c7-b8ca-d046984b0f35} + SuperVM + + + + + \ No newline at end of file diff --git a/SuperVM.VisualDebugger/VirtualMachineModel.cs b/SuperVM.VisualDebugger/VirtualMachineModel.cs new file mode 100644 index 0000000..eb2fc36 --- /dev/null +++ b/SuperVM.VisualDebugger/VirtualMachineModel.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Windows.Input; +using SuperVM.Assembler; + +namespace SuperVM.VisualDebugger +{ + public class VirtualMachineModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private Process process; + private VMAssembly assembly; + + public VirtualMachineModel() + { + this.process = new Process() + { + Memory = new Memory(1024), + }; + + this.Source = "\tpush 12\n\tpush 30\n\tadd\n\tdrop"; + + this.Recompile(); + + this.Reset(); + + + this.StepCommand = new RelayCommand(this.Step); + this.ResetCommand = new RelayCommand(this.Reset); + this.RecompileCommand = new RelayCommand(this.Recompile); + } + + public void Recompile() + { + this.assembly = Assembler.Assembler.Assemble(this.Source); + + this.process.Module = assembly.CreateModule(); + + this.Reset(); + } + + public void Reset() + { + this.process.Reset(); + this.OnVmChanged(); + } + + public void Step() + { + this.process.Step(); + 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.assembly.OriginalLine[this.CodePointer] - 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(0, (int)this.process.StackPointer) + .Select(i => new StackItem(i, this.process)) + .ToArray(); + + public string Source { get; set; } + + public ICommand StepCommand { get; private set; } + + public ICommand ResetCommand { get; private set; } + + public ICommand RecompileCommand { 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; } + } + } +} diff --git a/SuperVM.VisualDebugger/app.config b/SuperVM.VisualDebugger/app.config new file mode 100644 index 0000000..3dbff35 --- /dev/null +++ b/SuperVM.VisualDebugger/app.config @@ -0,0 +1,3 @@ + + + diff --git a/SuperVM.VisualDebugger/packages.config b/SuperVM.VisualDebugger/packages.config new file mode 100644 index 0000000..59134e6 --- /dev/null +++ b/SuperVM.VisualDebugger/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SuperVM.sln b/SuperVM.sln new file mode 100644 index 0000000..b348cd0 --- /dev/null +++ b/SuperVM.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperVM.VisualDebugger", "SuperVM.VisualDebugger\SuperVM.VisualDebugger.csproj", "{4947C531-73DC-4533-A960-E5970B0A7CCE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperVM", "SuperVM\SuperVM.csproj", "{014295B7-06BD-47C7-B8CA-D046984B0F35}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperVM.Assembler", "SuperVM.Assembler\SuperVM.Assembler.csproj", "{A84B2773-0EB8-4F3A-B342-68DDD9BD675E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4947C531-73DC-4533-A960-E5970B0A7CCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4947C531-73DC-4533-A960-E5970B0A7CCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4947C531-73DC-4533-A960-E5970B0A7CCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4947C531-73DC-4533-A960-E5970B0A7CCE}.Release|Any CPU.Build.0 = Release|Any CPU + {014295B7-06BD-47C7-B8CA-D046984B0F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {014295B7-06BD-47C7-B8CA-D046984B0F35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {014295B7-06BD-47C7-B8CA-D046984B0F35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {014295B7-06BD-47C7-B8CA-D046984B0F35}.Release|Any CPU.Build.0 = Release|Any CPU + {A84B2773-0EB8-4F3A-B342-68DDD9BD675E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A84B2773-0EB8-4F3A-B342-68DDD9BD675E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A84B2773-0EB8-4F3A-B342-68DDD9BD675E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A84B2773-0EB8-4F3A-B342-68DDD9BD675E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SuperVM/Enums.cs b/SuperVM/Enums.cs new file mode 100644 index 0000000..02bccce --- /dev/null +++ b/SuperVM/Enums.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SuperVM +{ + public enum MathCommand + { + Add = 0, + Subtract = 1, + Multiplicate = 2, + Divide = 3, + Modulo = 4, + And = 5, + Or = 6, + Xor = 7, + Not = 8, + RotShiftLeft = 9, + RotShiftRight = 10, + ArithmeticShiftLeft = 11, + ArithmeticShiftRight = 12, + LogicShiftLeft = 13, + LogicShiftRight = 14, + } + + public enum Command + { + Copy = 0, + Store = 1, + Load = 2, + Get = 3, + Set = 4, + BpGet = 5, + BpSet = 6, + CpGet = 7, + Math = 8, + SpGet = 9, + SpSet = 10, + SysCall = 11, + HwIO = 12, + } + + public enum OutputType + { + Discard = 0, + Push = 1, + Jump = 2, + JumpRelative = 3, + } + + public enum ExecutionMode + { + Always = 0, + Zero = 2, + One = 3, + } + + public enum InputType + { + Zero = 0, + Pop = 1, + Peek = 2, + Argument = 3, + } +} diff --git a/SuperVM/Instruction.cs b/SuperVM/Instruction.cs new file mode 100644 index 0000000..ce6090f --- /dev/null +++ b/SuperVM/Instruction.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SuperVM +{ + public struct Instruction + { + public ExecutionMode ExecutionZ; + public ExecutionMode ExecutionN; + public InputType Input0; + public InputType Input1; + public Command Command; + public ushort CommandInfo; + public bool ModifyFlags; + public OutputType Output; + public uint Argument; + } +} diff --git a/SuperVM/Memory.cs b/SuperVM/Memory.cs new file mode 100644 index 0000000..04790a1 --- /dev/null +++ b/SuperVM/Memory.cs @@ -0,0 +1,60 @@ +using System; + +namespace SuperVM +{ + public class Memory + { + private readonly byte[] data; + + public Memory(int size) + { + this.data = new byte[size]; + } + + public byte GetUInt8(uint address) + { + return this.data[address]; + } + + public UInt16 GetUInt16(uint address) + { + return BitConverter.ToUInt16(this.data, (int)address); + } + + public UInt32 GetUint32(uint address) + { + return BitConverter.ToUInt32(this.data, (int)address); + } + + public void SetUInt8(uint address, byte value) + { + this.data[address] = value; + } + + public void SetUInt16(uint address, UInt16 value) + { + Array.Copy( + BitConverter.GetBytes(value), 0, + this.data, address, + 2); + } + + public void SetUint32(uint address, UInt32 value) + { + Array.Copy( + BitConverter.GetBytes(value), 0, + this.data, address, + 4); + } + + public byte this[uint address] + { + get { return this.data[address]; } + set { this.data[address] = value; } + } + + public byte[] Raw => this.data; + + public int Size => this.data.Length; + } +} \ No newline at end of file diff --git a/SuperVM/Module.cs b/SuperVM/Module.cs new file mode 100644 index 0000000..62e5f09 --- /dev/null +++ b/SuperVM/Module.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace SuperVM +{ + public class Module + { + private readonly Instruction[] code; + + public Module(Instruction[] code) + { + this.code = code; + } + + public IReadOnlyList Code => this.code; + } +} \ No newline at end of file diff --git a/SuperVM/Process.Commands.cs b/SuperVM/Process.Commands.cs new file mode 100644 index 0000000..4487187 --- /dev/null +++ b/SuperVM/Process.Commands.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SuperVM +{ + partial class Process + { + private static class Cmd + { + + public static void Copy(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Input0; + } + + public static void Load(CommandExecutionEnvironment cxe) + { + switch (cxe.Additional) + { + case 0: cxe.Output = cxe.Process.Memory.GetUInt8(cxe.Input0); break; + case 1: cxe.Output = cxe.Process.Memory.GetUInt16(cxe.Input0); break; + case 2: cxe.Output = cxe.Process.Memory.GetUint32(cxe.Input0); break; + default: throw new InvalidOperationException(); + } + } + + public static void Store(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Input1; + switch (cxe.Additional) + { + case 0: + { + cxe.Process.Memory.SetUInt8(cxe.Input0, (byte)cxe.Input1); + cxe.Output &= 0xFF; + break; + } + case 1: + { + cxe.Process.Memory.SetUInt16(cxe.Input0, (ushort)cxe.Input1); + cxe.Output &= 0xFFFF; + break; + } + case 2: + { + cxe.Process.Memory.SetUint32(cxe.Input0, cxe.Input1); + break; + } + default: throw new InvalidOperationException(); + } + } + + public static void SpGet(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.StackPointer; + } + + public static void SpSet(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.StackPointer = cxe.Input0; + } + + public static void BpGet(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.BasePointer; + } + + public static void BpSet(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.BasePointer = cxe.Input0; + } + + public static void CpGet(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.CodePointer + cxe.Additional; + } + + public static void Get(CommandExecutionEnvironment cxe) + { + unchecked + { + cxe.Output = cxe.Process.stack[cxe.Process.BasePointer + (int)cxe.Input0]; + } + } + + public static void Set(CommandExecutionEnvironment cxe) + { + cxe.Output = cxe.Process.stack[cxe.Process.BasePointer + (int)cxe.Input0] = cxe.Input1; + } + + public static void Math(CommandExecutionEnvironment cxe) + { + switch ((MathCommand)cxe.Additional) + { + case MathCommand.Add: cxe.Output = cxe.Input1 + cxe.Input0; break; + case MathCommand.Subtract: cxe.Output = cxe.Input1 - cxe.Input0; break; + case MathCommand.Multiplicate: cxe.Output = cxe.Input1 * cxe.Input0; break; + case MathCommand.Divide: cxe.Output = cxe.Input1 / cxe.Input0; break; + case MathCommand.Modulo: cxe.Output = cxe.Input1 % cxe.Input0; break; + case MathCommand.And: cxe.Output = cxe.Input1 & cxe.Input0; break; + case MathCommand.Or: cxe.Output = cxe.Input1 | cxe.Input0; break; + case MathCommand.Xor: cxe.Output = cxe.Input1 ^ cxe.Input0; break; + case MathCommand.Not: cxe.Output = ~cxe.Input0; break; + } + } + } + } +} diff --git a/SuperVM/Process.cs b/SuperVM/Process.cs new file mode 100644 index 0000000..d858b5f --- /dev/null +++ b/SuperVM/Process.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SuperVM +{ + public sealed partial class Process + { + public const int StackSize = 1024; + + private readonly uint[] stack = new uint[StackSize]; + + public event EventHandler SysCall; + + public event EventHandler HardwareIO; + + public Process() + { + this.CodePointer = 0; + this.StackPointer = 0; + this.BasePointer = 0; + this.ZFlag = false; + this.NFlag = false; + } + + public void Push(uint value) + { + checked + { + this.stack[this.StackPointer++] = value; + } + } + + public uint Pop() + { + checked + { + return this.stack[--this.StackPointer]; + } + } + + public uint Peek() => this.stack[this.StackPointer - 1]; + + public void Reset() + { + this.CodePointer = 0; + this.BasePointer = 0; + this.StackPointer = 0; + this.NFlag = false; + this.ZFlag = false; + } + + public void Step() + { + Instruction instr = this.Module.Code[(int)this.CodePointer++]; + + bool exec = true; + switch (instr.ExecutionZ) + { + case ExecutionMode.Always: + {/* Don't modify execution. */ + break; + } + case ExecutionMode.Zero: + { + if (this.ZFlag) + exec = false; + break; + } + case ExecutionMode.One: + { + if (!this.ZFlag) + exec = false; + break; + } + default: throw new InvalidOperationException("Invalid instruction: execZ undefined."); + } + switch (instr.ExecutionN) + { + case ExecutionMode.Always: + {/* Don't modify execution. */ + break; + } + case ExecutionMode.Zero: + { + if (this.NFlag) + exec = false; + break; + } + case ExecutionMode.One: + { + if (!this.NFlag) + exec = false; + break; + } + default: throw new InvalidOperationException("Invalid instruction: execZ undefined."); + } + + // Only do further instruction execution when + // the execution condition is met. + if (exec) + { + var cxe = new CommandExecutionEnvironment(this); + switch (instr.Input0) + { + case InputType.Zero: cxe.Input0 = 0; break; + case InputType.Pop: cxe.Input0 = this.Pop(); break; + case InputType.Peek: cxe.Input0 = this.Peek(); break; + case InputType.Argument: cxe.Input0 = instr.Argument; break; + default: throw new InvalidOperationException("Invalid instruction: input0 undefined."); + } + + switch (instr.Input1) + { + case InputType.Zero: cxe.Input1 = 0; break; + case InputType.Pop: cxe.Input1 = this.Pop(); break; + default: throw new InvalidOperationException("Invalid instruction: input1 undefined."); + } + + cxe.Argument = instr.Argument; + cxe.Additional = instr.CommandInfo; + + switch (instr.Command) + { + case Command.Copy: Cmd.Copy(cxe); break; + case Command.Store: Cmd.Store(cxe); break; + case Command.Load: Cmd.Load(cxe); break; + case Command.Math: Cmd.Math(cxe); break; + case Command.SysCall: this.SysCall?.Invoke(this, cxe); break; + case Command.HwIO: this.HardwareIO?.Invoke(this, cxe); break; + case Command.SpGet: Cmd.SpGet(cxe); break; + case Command.SpSet: Cmd.SpSet(cxe); break; + case Command.BpGet: Cmd.BpGet(cxe); break; + case Command.BpSet: Cmd.BpSet(cxe); break; + case Command.CpGet: Cmd.CpGet(cxe); break; + case Command.Get: Cmd.Get(cxe); break; + case Command.Set: Cmd.Set(cxe); break; + default: throw new InvalidOperationException("Invalid instruction: command undefined."); + } + + if (instr.ModifyFlags) + { + this.ZFlag = (cxe.Output == 0); + this.NFlag = (cxe.Output & (1 << 31)) != 0; + } + + switch (instr.Output) + { + case OutputType.Discard: break; + case OutputType.Push: this.Push(cxe.Output); break; + case OutputType.Jump: this.CodePointer = cxe.Output; break; + case OutputType.JumpRelative: + { + checked + { + this.CodePointer = (uint)(this.CodePointer + (int)cxe.Output); + } + break; + } + default: throw new NotSupportedException("Invalid instruction: invalid output."); + } + } + + } + + public Module Module { get; set; } + + public Memory Memory { get; set; } + + public uint CodePointer { get; set; } + public uint StackPointer { get; set; } + public uint BasePointer { get; set; } + + public bool ZFlag { get; set; } + + public bool NFlag { get; set; } + + public uint[] Stack => this.stack; + + + + + public sealed class CommandExecutionEnvironment : EventArgs + { + public CommandExecutionEnvironment(Process process) + { + this.Process = process; + } + + public uint Input0 { get; set; } + public uint Input1 { get; set; } + public uint Argument { get; set; } + public uint Additional { get; set; } + public uint Output { get; set; } + + public Process Process { get; private set; } + } + } +} diff --git a/SuperVM/Properties/AssemblyInfo.cs b/SuperVM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ee8f136 --- /dev/null +++ b/SuperVM/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("SuperVM")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SuperVM")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("014295b7-06bd-47c7-b8ca-d046984b0f35")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SuperVM/SuperVM.csproj b/SuperVM/SuperVM.csproj new file mode 100644 index 0000000..e810683 --- /dev/null +++ b/SuperVM/SuperVM.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {014295B7-06BD-47C7-B8CA-D046984B0F35} + Library + Properties + SuperVM + SuperVM + v4.6 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file