Initial Commit
This commit is contained in:
25
SVM.sln
Normal file
25
SVM.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27130.2010
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVM", "SVM\SVM.csproj", "{C8C1E9F3-DAC3-4779-AF30-352DBE4DAB4C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{C8C1E9F3-DAC3-4779-AF30-352DBE4DAB4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C8C1E9F3-DAC3-4779-AF30-352DBE4DAB4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C8C1E9F3-DAC3-4779-AF30-352DBE4DAB4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C8C1E9F3-DAC3-4779-AF30-352DBE4DAB4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {0357165B-31C2-41A9-8767-32104597595E}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
48
SVM/ArrayExtensions.cs
Normal file
48
SVM/ArrayExtensions.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
static class ArrayExtensions
|
||||||
|
{
|
||||||
|
public static byte[] Subset(this byte[] array, int start, int length)
|
||||||
|
{
|
||||||
|
Debug.Assert(array.Length >= start + length);
|
||||||
|
byte[] subset = new byte[length];
|
||||||
|
Array.Copy(array, start, subset, 0, length);
|
||||||
|
return subset;
|
||||||
|
}
|
||||||
|
public static byte[] Subset(this byte[] array, int start)
|
||||||
|
{
|
||||||
|
Debug.Assert(array.Length >= start);
|
||||||
|
byte[] subset = new byte[array.Length - start];
|
||||||
|
Array.Copy(array, start, subset, 0, array.Length - start);
|
||||||
|
return subset;
|
||||||
|
}
|
||||||
|
public static byte[] Concat(this byte[] array, params byte[][] others)
|
||||||
|
{
|
||||||
|
var size = array.Length + others.Sum(o => o.Length);
|
||||||
|
var result = new byte[size];
|
||||||
|
|
||||||
|
var i = array.Length;
|
||||||
|
Array.Copy(array, 0, result, 0, array.Length);
|
||||||
|
foreach(var o in others)
|
||||||
|
{
|
||||||
|
Array.Copy(o, 0, result, i, o.Length);
|
||||||
|
i += o.Length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public static byte[] Prepend(this byte[] array, byte val)
|
||||||
|
{
|
||||||
|
var newArr = new byte[array.Length + 1];
|
||||||
|
var len = array.Length;
|
||||||
|
Array.Copy(array, 0, newArr, 1, array.Length);
|
||||||
|
newArr[0] = val;
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
166
SVM/Assembler.cs
Normal file
166
SVM/Assembler.cs
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
class Assembler
|
||||||
|
{
|
||||||
|
Instruction[] instructions;
|
||||||
|
|
||||||
|
public Assembler()
|
||||||
|
{
|
||||||
|
instructions = Instruction.GetAllInstructions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Compile(string program)
|
||||||
|
{
|
||||||
|
byte[] mem = new byte[VM.MEMSIZE];
|
||||||
|
|
||||||
|
var lines = (from l in program.Replace("\t", " ").Split(Environment.NewLine)
|
||||||
|
where !l.StartsWith('#') && !String.IsNullOrWhiteSpace(l)
|
||||||
|
select l.Split('#').First().Trim().ToUpper()).ToArray();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
ushort mempos = 0;
|
||||||
|
ushort lastpos = 0;
|
||||||
|
string line;
|
||||||
|
string[] parts;
|
||||||
|
Dictionary<string, ushort> markers = new Dictionary<string, ushort>();
|
||||||
|
Dictionary<string, List<ushort>> markerUses = new Dictionary<string, List<ushort>>();
|
||||||
|
//Encode ops until memory section
|
||||||
|
for (; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
line = lines[i];
|
||||||
|
if (line == "MEMORY")
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove multiple spaces
|
||||||
|
while (line.IndexOf(" ") != -1)
|
||||||
|
{
|
||||||
|
line = line.Replace(" ", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Records marker locations
|
||||||
|
if (line.StartsWith(':'))
|
||||||
|
{
|
||||||
|
markers.Add(line.Substring(1), mempos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = line.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var op = parts[0];
|
||||||
|
if (op == "ORIGIN")
|
||||||
|
{
|
||||||
|
Debug.Assert(parts.Length == 2);
|
||||||
|
if (parts[1].StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
mempos = ushort.Parse(parts[1].Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mempos = ushort.Parse(parts[1]);
|
||||||
|
}
|
||||||
|
if (mempos > lastpos) lastpos = mempos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instr = instructions.First(x => x.ASM == op);
|
||||||
|
|
||||||
|
Dictionary<string, ushort> markerRefs = new Dictionary<string, ushort>();
|
||||||
|
var bytecode = instr.Encode(parts.Length > 1 ? parts[1] : string.Empty, markerRefs);
|
||||||
|
if (markerRefs != null && markerRefs.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var mRef in markerRefs)
|
||||||
|
{
|
||||||
|
if (markerUses.ContainsKey(mRef.Key))
|
||||||
|
{
|
||||||
|
markerUses[mRef.Key].Add((ushort)(mempos + mRef.Value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
markerUses.Add(mRef.Key, new List<ushort>() { (ushort)(mempos + mRef.Value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Array.Copy(bytecode, 0, mem, mempos, bytecode.Length);
|
||||||
|
mempos += (ushort)bytecode.Length;
|
||||||
|
if (mempos > lastpos) lastpos = mempos;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var mUse in markerUses)
|
||||||
|
{
|
||||||
|
if (!markers.ContainsKey(mUse.Key))
|
||||||
|
{
|
||||||
|
throw new Exception(string.Format("Use of undefined marker {0}", mUse.Key));
|
||||||
|
}
|
||||||
|
foreach(var loc in mUse.Value)
|
||||||
|
{
|
||||||
|
mem[loc] = markers[mUse.Key].HiByte();
|
||||||
|
mem[loc+1] = markers[mUse.Key].LoByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
line = lines[i];
|
||||||
|
parts = line.Split(" ", 2);
|
||||||
|
|
||||||
|
ushort dataOrigin = 0;
|
||||||
|
if (parts[0].StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
dataOrigin = ushort.Parse(parts[0].Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataOrigin = ushort.Parse(parts[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] lineData = new byte[0];
|
||||||
|
if (parts[1].StartsWith('\'') || parts[1].StartsWith('"'))
|
||||||
|
{
|
||||||
|
string asciiContent = string.Empty;
|
||||||
|
if (parts[1].StartsWith('\''))
|
||||||
|
{
|
||||||
|
asciiContent = parts[1].Trim('\'');
|
||||||
|
}
|
||||||
|
else if (parts[1].StartsWith('"'))
|
||||||
|
{
|
||||||
|
asciiContent = parts[1].Trim('"');
|
||||||
|
}
|
||||||
|
lineData = Encoding.ASCII.GetBytes(asciiContent);
|
||||||
|
//Zero terminate
|
||||||
|
Array.Resize(ref lineData, lineData.Length + 1);
|
||||||
|
lineData[lineData.Length - 1] = 0;
|
||||||
|
}
|
||||||
|
else if (parts[1].StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var hex = parts[1].Substring(2).Replace(" ", "");
|
||||||
|
if (hex.Length % 2 != 0)
|
||||||
|
{
|
||||||
|
hex = "0" + hex;
|
||||||
|
}
|
||||||
|
lineData = new byte[hex.Length / 2];
|
||||||
|
for (int di = 0, ldi = 0; di < hex.Length; di += 2, ldi++)
|
||||||
|
{
|
||||||
|
lineData[ldi] = byte.Parse(hex.Substring(di, 2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Array.Copy(lineData, 0, mem, dataOrigin, lineData.Length);
|
||||||
|
if (lastpos < dataOrigin + lineData.Length)
|
||||||
|
{
|
||||||
|
lastpos = (ushort)(dataOrigin + lineData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Resize<byte>(ref mem, lastpos);
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
SVM/Instruction.cs
Normal file
42
SVM/Instruction.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
abstract class Instruction
|
||||||
|
{
|
||||||
|
public static Instruction[] GetAllInstructions()
|
||||||
|
{
|
||||||
|
var types = from t in Assembly.GetAssembly(typeof(Instruction)).GetTypes()
|
||||||
|
where t.IsSubclassOf(typeof(Instruction))
|
||||||
|
select t;
|
||||||
|
|
||||||
|
var instr = new List<Instruction>();
|
||||||
|
|
||||||
|
foreach(var t in types)
|
||||||
|
{
|
||||||
|
instr.Add((Instruction)Activator.CreateInstance(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return instr.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract string ASM { get; }
|
||||||
|
public abstract byte OP { get; }
|
||||||
|
|
||||||
|
public virtual byte[] Encode(string asm)
|
||||||
|
{
|
||||||
|
return Encode(asm, null);
|
||||||
|
}
|
||||||
|
public abstract byte[] Encode(string asm, Dictionary<string, ushort> markerRefs);
|
||||||
|
public abstract string ToASM(byte[] vars);
|
||||||
|
public abstract void Exec(VM vm, byte[] vars);
|
||||||
|
public virtual byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
SVM/Instructions/ADD.cs
Normal file
56
SVM/Instructions/ADD.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class ADD : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "ADD";
|
||||||
|
|
||||||
|
public override byte OP => 0x21;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
var parts = asm.Split(" ", 2);
|
||||||
|
Debug.Assert(parts.Length == 2);
|
||||||
|
var reg = Register.FromASM(parts[0]);
|
||||||
|
|
||||||
|
var bc = new byte[] { OP, reg };
|
||||||
|
|
||||||
|
return bc.Concat(Location.FromASM(parts[1]).Encode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var pc = vm.PC;
|
||||||
|
vm.PC += 1 + Location.SIZE;
|
||||||
|
return vm.MEM.Subset(pc, 1 + Location.SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length > 1);
|
||||||
|
var reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
var loc = Location.FromByteCode(vars, 1);
|
||||||
|
|
||||||
|
Run(vm, reg, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
vm.R[reg] += loc.Read(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 1 + Location.SIZE);
|
||||||
|
var reg = vars[0];
|
||||||
|
var loc = Location.FromByteCode(vars, 1);
|
||||||
|
return string.Format("{0} {1} {2}", ASM, Register.ToASM(reg), loc.ToASM());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
SVM/Instructions/CALL.cs
Normal file
56
SVM/Instructions/CALL.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class CALL : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "CALL";
|
||||||
|
|
||||||
|
public override byte OP => 0x40;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
ushort jmp = 0;
|
||||||
|
if (asm.StartsWith(':'))
|
||||||
|
{
|
||||||
|
markerRefs.Add(asm.Substring(1), 1);
|
||||||
|
}
|
||||||
|
else if (asm.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
jmp = ushort.Parse(asm.Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jmp = ushort.Parse(asm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new byte[] { OP, jmp.HiByte(), jmp.LoByte() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var bc = vm.MEM.Subset(vm.PC, 2);
|
||||||
|
vm.PC += 2;
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 2);
|
||||||
|
|
||||||
|
Debug.Assert(vm.SP < VM.STACKDEPTH);
|
||||||
|
|
||||||
|
vm.STACK[vm.SP++] = vm.PC;
|
||||||
|
vm.PC = (ushort)((vars[0] << 8) + vars[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
var jmp = (ushort)((vars[0] << 8) + vars[1]);
|
||||||
|
return string.Format("{0} 0x{1:X}", ASM, jmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
SVM/Instructions/CLR.cs
Normal file
44
SVM/Instructions/CLR.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class CLR : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "CLR";
|
||||||
|
|
||||||
|
public override byte OP => 0x20;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
byte reg = Register.FromASM(asm);
|
||||||
|
return new byte[] { OP, reg };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
return new byte[] { vm.MEM[vm.PC++] };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 1);
|
||||||
|
byte reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
Run(vm, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.R[reg] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 1);
|
||||||
|
return string.Format("{0} {1}", ASM, Register.ToASM(vars[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SVM/Instructions/CLRI.cs
Normal file
31
SVM/Instructions/CLRI.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class CLRI : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "CLRI";
|
||||||
|
|
||||||
|
public override byte OP => 0x62;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
vm.RI = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
return ASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SVM/Instructions/DEC.cs
Normal file
19
SVM/Instructions/DEC.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class DEC : CLR
|
||||||
|
{
|
||||||
|
public override string ASM => "DEC";
|
||||||
|
|
||||||
|
public override byte OP => 0x26;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.R[reg]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SVM/Instructions/DECI.cs
Normal file
31
SVM/Instructions/DECI.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class DECI : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "DECI";
|
||||||
|
|
||||||
|
public override byte OP => 0x64;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
vm.RI--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
return ASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/DIV.cs
Normal file
18
SVM/Instructions/DIV.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class DIV : ADD
|
||||||
|
{
|
||||||
|
public override string ASM => "DIV";
|
||||||
|
|
||||||
|
public override byte OP => 0x23;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
vm.R[reg] /= loc.Read(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SVM/Instructions/HALT.cs
Normal file
31
SVM/Instructions/HALT.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class HALT : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "HALT";
|
||||||
|
|
||||||
|
public override byte OP => 0x50;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
vm.RUN = false;
|
||||||
|
vm.Ports[0].Write(Encoding.ASCII.GetBytes("\r\nSYSTEM HALTED"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
return ASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SVM/Instructions/INC.cs
Normal file
19
SVM/Instructions/INC.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class INC : CLR
|
||||||
|
{
|
||||||
|
public override string ASM => "INC";
|
||||||
|
|
||||||
|
public override byte OP => 0x25;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.R[reg]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SVM/Instructions/INCI.cs
Normal file
31
SVM/Instructions/INCI.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class INCI : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "INCI";
|
||||||
|
|
||||||
|
public override byte OP => 0x63;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
vm.RI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
return ASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
SVM/Instructions/JMP.cs
Normal file
54
SVM/Instructions/JMP.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class JMP : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "JMP";
|
||||||
|
|
||||||
|
public override byte OP => 0x51;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
ushort loc = 0;
|
||||||
|
|
||||||
|
if (asm.StartsWith(':') && markerRefs != null)
|
||||||
|
{
|
||||||
|
markerRefs.Add(asm.Substring(1), 1);
|
||||||
|
}
|
||||||
|
else if (asm.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
loc = ushort.Parse(asm.Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loc = ushort.Parse(asm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new byte[] { OP, loc.HiByte(), loc.LoByte() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var code = vm.MEM.Subset(vm.PC, 2);
|
||||||
|
vm.PC += 2;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 2);
|
||||||
|
ushort loc = (ushort)((vars[0] << 8) + vars[1]);
|
||||||
|
vm.PC = loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
ushort loc = (ushort)((vars[0] << 8) + vars[1]);
|
||||||
|
return string.Format("{0} 0x{1:x}", ASM, loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
SVM/Instructions/JNZ.cs
Normal file
21
SVM/Instructions/JNZ.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class JNZ : JZ
|
||||||
|
{
|
||||||
|
public override string ASM => "JNZ";
|
||||||
|
|
||||||
|
public override byte OP => 0x53;
|
||||||
|
|
||||||
|
public override void CheckJump(VM vm, byte reg, ushort loc)
|
||||||
|
{
|
||||||
|
if (vm.R[reg] != 0)
|
||||||
|
{
|
||||||
|
vm.PC = loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
SVM/Instructions/JZ.cs
Normal file
71
SVM/Instructions/JZ.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class JZ : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "JZ";
|
||||||
|
|
||||||
|
public override byte OP => 0x52;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
var parts = asm.Split(" ", 2);
|
||||||
|
Debug.Assert(parts.Length == 2);
|
||||||
|
|
||||||
|
var reg = Register.FromASM(parts[0]);
|
||||||
|
|
||||||
|
ushort loc = 0;
|
||||||
|
|
||||||
|
if (parts[1].StartsWith(':') && markerRefs != null)
|
||||||
|
{
|
||||||
|
markerRefs.Add(parts[1].Substring(1), 2);
|
||||||
|
}
|
||||||
|
else if (parts[1].StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
loc = ushort.Parse(parts[1].Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loc = ushort.Parse(parts[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new byte[] { OP, reg, loc.HiByte(), loc.LoByte() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var code = vm.MEM.Subset(vm.PC, 3);
|
||||||
|
vm.PC += 3;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 3);
|
||||||
|
byte reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
ushort loc = (ushort)((vars[1] << 8) + vars[2]);
|
||||||
|
|
||||||
|
CheckJump(vm, reg, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void CheckJump(VM vm, byte reg, ushort loc)
|
||||||
|
{
|
||||||
|
if (vm.R[reg] == 0)
|
||||||
|
{
|
||||||
|
vm.PC = loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
var reg = vars[0];
|
||||||
|
ushort loc = (ushort)((vars[1] << 8) + vars[2]);
|
||||||
|
return string.Format("{0} {1} 0x{2:x}", ASM, Register.ToASM(reg), loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
SVM/Instructions/LOAD.cs
Normal file
56
SVM/Instructions/LOAD.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class LOAD : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "LOAD";
|
||||||
|
|
||||||
|
public override byte OP => 0x10;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
var parts = asm.Split(" ");
|
||||||
|
Debug.Assert(parts.Length > 1);
|
||||||
|
var reg = Register.FromASM(parts[0]);
|
||||||
|
var loc = Location.FromASM(String.Join(" ", parts.Skip(1)));
|
||||||
|
return (new byte[] { OP, reg }).Concat(loc.Encode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var pc = vm.PC;
|
||||||
|
ushort size = 1 + Location.SIZE;
|
||||||
|
vm.PC += size;
|
||||||
|
return vm.MEM.Subset(pc, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length > 1);
|
||||||
|
var reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
var loc = Location.FromByteCode(vars, 1);
|
||||||
|
Run(vm, reg, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
vm.R[reg] = loc.Read(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length > 1);
|
||||||
|
var reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
var loc = Location.FromByteCode(vars, 1);
|
||||||
|
|
||||||
|
return string.Format("{0} {1} {2}", ASM, Register.ToASM(reg), loc.ToASM());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/LOADH.cs
Normal file
18
SVM/Instructions/LOADH.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class LOADH : LOAD
|
||||||
|
{
|
||||||
|
public override string ASM => "LOADH";
|
||||||
|
public override byte OP => 0x12;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
var data = loc.Read(vm);
|
||||||
|
vm.R[reg] = (ushort)((data << 8) + (vm.R[reg] & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
SVM/Instructions/LOADI.cs
Normal file
45
SVM/Instructions/LOADI.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class LOADI : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "LOADI";
|
||||||
|
|
||||||
|
public override byte OP => 0x60;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
var reg = Register.FromASM(asm);
|
||||||
|
return new byte[] { OP, reg };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] Decode(VM vm)
|
||||||
|
{
|
||||||
|
var reg = vm.MEM[vm.PC];
|
||||||
|
vm.PC++;
|
||||||
|
return new byte[] { reg };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 1);
|
||||||
|
var reg = vars[0];
|
||||||
|
Debug.Assert(reg <= VM.REGISTERS);
|
||||||
|
Run(vm, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.R[reg] = vm.MEM[vm.RI];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
return string.Format("{0} {1}", ASM, Register.ToASM(vars[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/LOADL.cs
Normal file
18
SVM/Instructions/LOADL.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class LOADL : LOAD
|
||||||
|
{
|
||||||
|
public override string ASM => "LOADL";
|
||||||
|
public override byte OP => 0x13;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
var data = loc.Read(vm);
|
||||||
|
vm.R[reg] = (ushort)((vm.R[reg] & 0xFF00) + data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/MUL.cs
Normal file
18
SVM/Instructions/MUL.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class MUL : ADD
|
||||||
|
{
|
||||||
|
public override string ASM => "SUB";
|
||||||
|
|
||||||
|
public override byte OP => 0x24;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
vm.R[reg] *= loc.Read(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SVM/Instructions/NOP.cs
Normal file
31
SVM/Instructions/NOP.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class NOP : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "NOOP";
|
||||||
|
|
||||||
|
public override byte OP => 0x00;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
return ASM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
SVM/Instructions/RET.cs
Normal file
33
SVM/Instructions/RET.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class RET : Instruction
|
||||||
|
{
|
||||||
|
public override string ASM => "RET";
|
||||||
|
|
||||||
|
public override byte OP => 0x41;
|
||||||
|
|
||||||
|
public override byte[] Encode(string asm, Dictionary<string, ushort> markerRefs)
|
||||||
|
{
|
||||||
|
return new byte[] { OP };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Exec(VM vm, byte[] vars)
|
||||||
|
{
|
||||||
|
Debug.Assert(vars.Length == 0);
|
||||||
|
Debug.Assert(vm.SP >= 0);
|
||||||
|
|
||||||
|
vm.PC = vm.STACK[vm.SP];
|
||||||
|
vm.STACK[vm.SP--] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToASM(byte[] vars)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
SVM/Instructions/SAVE.cs
Normal file
20
SVM/Instructions/SAVE.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SAVE : LOAD
|
||||||
|
{
|
||||||
|
public override string ASM => "SAVE";
|
||||||
|
|
||||||
|
public override byte OP => 0x11;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
loc.Write(vm, vm.R[reg]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/SAVEH.cs
Normal file
18
SVM/Instructions/SAVEH.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SAVEH : LOAD
|
||||||
|
{
|
||||||
|
public override string ASM => "SAVEH";
|
||||||
|
public override byte OP => 0x14;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
byte data = (byte)((vm.R[reg] & 0xFF00) >> 8);
|
||||||
|
loc.Write(vm, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SVM/Instructions/SAVEI.cs
Normal file
19
SVM/Instructions/SAVEI.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SAVEI : LOADI
|
||||||
|
{
|
||||||
|
public override string ASM => "SAVEI";
|
||||||
|
|
||||||
|
public override byte OP => 0x61;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.MEM[vm.RI] = (byte)vm.R[reg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/SAVEL.cs
Normal file
18
SVM/Instructions/SAVEL.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SAVEL : LOAD
|
||||||
|
{
|
||||||
|
public override string ASM => "SAVEL";
|
||||||
|
public override byte OP => 0x15;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
byte data = (byte)(vm.R[reg] & 0xFF);
|
||||||
|
loc.Write(vm, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SVM/Instructions/SETI.cs
Normal file
19
SVM/Instructions/SETI.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SETI : LOADI
|
||||||
|
{
|
||||||
|
public override string ASM => "SETI";
|
||||||
|
|
||||||
|
public override byte OP => 0x65;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg)
|
||||||
|
{
|
||||||
|
vm.RI = vm.R[reg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/Instructions/SUB.cs
Normal file
18
SVM/Instructions/SUB.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Instructions
|
||||||
|
{
|
||||||
|
class SUB : ADD
|
||||||
|
{
|
||||||
|
public override string ASM => "SUB";
|
||||||
|
|
||||||
|
public override byte OP => 0x22;
|
||||||
|
|
||||||
|
protected override void Run(VM vm, byte reg, Location loc)
|
||||||
|
{
|
||||||
|
vm.R[reg] -= loc.Read(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
135
SVM/Location.cs
Normal file
135
SVM/Location.cs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
class Location
|
||||||
|
{
|
||||||
|
public const byte PORT_CODE = 0x0;
|
||||||
|
public const string PORT_ASM = "P";
|
||||||
|
public const byte REG_CODE = 0x1;
|
||||||
|
public const string REG_ASM = "R";
|
||||||
|
public const byte MEM_CODE = 0x2;
|
||||||
|
public const string MEM_ASM = "M";
|
||||||
|
public const byte LITERAL_CODE = 0x3;
|
||||||
|
public const string LITERAL_ASM = "L";
|
||||||
|
|
||||||
|
public const int SIZE = 3;
|
||||||
|
|
||||||
|
public static byte EncodeType(string type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PORT_ASM: return PORT_CODE;
|
||||||
|
case REG_ASM: return REG_CODE;
|
||||||
|
case MEM_ASM: return MEM_CODE;
|
||||||
|
case LITERAL_ASM: return LITERAL_CODE;
|
||||||
|
default: throw new Exception("Invalid location type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string DecodeType(byte type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PORT_CODE: return PORT_ASM;
|
||||||
|
case REG_CODE: return REG_ASM;
|
||||||
|
case MEM_CODE: return MEM_ASM;
|
||||||
|
case LITERAL_CODE: return LITERAL_ASM;
|
||||||
|
default: throw new Exception("Invalid location type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Location FromASM(string asm)
|
||||||
|
{
|
||||||
|
var parts = asm.Split(" ", 2);
|
||||||
|
Debug.Assert(parts.Length > 0);
|
||||||
|
byte type = EncodeType(parts[0]);
|
||||||
|
ushort loc = 0;
|
||||||
|
Debug.Assert(parts.Length == 2);
|
||||||
|
string locVal = parts[1];
|
||||||
|
if (locVal.StartsWith('\'') || locVal.StartsWith('"'))
|
||||||
|
{
|
||||||
|
Debug.Assert(locVal.Length > 1);
|
||||||
|
loc = (byte)locVal[1];
|
||||||
|
}
|
||||||
|
else if (locVal.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
Debug.Assert(locVal.Length > 2);
|
||||||
|
loc = ushort.Parse(locVal.Substring(2), System.Globalization.NumberStyles.HexNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loc = ushort.Parse(locVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Location(type, loc);
|
||||||
|
}
|
||||||
|
public static Location FromByteCode(byte[] data, int start)
|
||||||
|
{
|
||||||
|
Debug.Assert(data.Length > start);
|
||||||
|
var type = data[start];
|
||||||
|
ushort loc = 0;
|
||||||
|
Debug.Assert(data.Length > start + 1);
|
||||||
|
loc = (ushort)((data[start + 1] << 8) + data[start + 2]);
|
||||||
|
return new Location(type, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte type;
|
||||||
|
private ushort loc;
|
||||||
|
|
||||||
|
public Location(byte type, ushort loc)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.loc = loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort Read(VM vm)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PORT_CODE:
|
||||||
|
return vm.Ports[loc].Read();
|
||||||
|
case REG_CODE:
|
||||||
|
return vm.R[loc];
|
||||||
|
case MEM_CODE:
|
||||||
|
return vm.MEM[loc];
|
||||||
|
case LITERAL_CODE:
|
||||||
|
return loc;
|
||||||
|
default: throw new Exception("Invalid location type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Write(VM vm, ushort val)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PORT_CODE:
|
||||||
|
vm.Ports[loc].Write((byte)val);
|
||||||
|
break;
|
||||||
|
case REG_CODE:
|
||||||
|
vm.R[loc] = val;
|
||||||
|
break;
|
||||||
|
case MEM_CODE:
|
||||||
|
vm.MEM[loc] = (byte)val;
|
||||||
|
break;
|
||||||
|
case LITERAL_CODE:
|
||||||
|
throw new Exception("Invalid operation");
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid location type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Encode()
|
||||||
|
{
|
||||||
|
byte ub = (byte)((loc & 0xFF00) >> 8);
|
||||||
|
byte lb = (byte)(loc & 0xFF);
|
||||||
|
return new byte[] { type, ub, lb };
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToASM()
|
||||||
|
{
|
||||||
|
return string.Format("{0} 0x{1:X}", DecodeType(type), loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SVM/NumberExtensions.cs
Normal file
19
SVM/NumberExtensions.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
static class NumberExtensions
|
||||||
|
{
|
||||||
|
public static byte HiByte(this ushort num)
|
||||||
|
{
|
||||||
|
return (byte)((num & 0xFF00) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte LoByte(this ushort num)
|
||||||
|
{
|
||||||
|
return (byte)(num & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
SVM/PGM/A.txt
Normal file
41
SVM/PGM/A.txt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
ORIGIN 0
|
||||||
|
#Print string 5 times
|
||||||
|
|
||||||
|
CLR B
|
||||||
|
ADD B L 5
|
||||||
|
ADD B L 48
|
||||||
|
SAVE B P 0
|
||||||
|
SUB B L 48
|
||||||
|
|
||||||
|
LOAD A L 0x300 #Set string start
|
||||||
|
SETI A
|
||||||
|
JMP :PRINTA
|
||||||
|
|
||||||
|
:PRINTA #Print until zero
|
||||||
|
LOADI A
|
||||||
|
JZ A :NEWLINE
|
||||||
|
SAVE A P 0
|
||||||
|
INCI
|
||||||
|
JMP :PRINTA
|
||||||
|
|
||||||
|
:NEWLINE #New Line
|
||||||
|
LOAD A L 13
|
||||||
|
SAVE A P 0
|
||||||
|
LOAD A L 10
|
||||||
|
SAVE A P 0
|
||||||
|
|
||||||
|
LOAD A L 0x300 #Set string start
|
||||||
|
SETI A
|
||||||
|
|
||||||
|
DEC B #Check Counter and stop if 5 printed
|
||||||
|
JZ B :STOP
|
||||||
|
ADD B L 48
|
||||||
|
SAVE B P 0
|
||||||
|
SUB B L 48
|
||||||
|
JMP :PRINTA
|
||||||
|
|
||||||
|
:STOP
|
||||||
|
HALT
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
0x300 " HELLO WORLD!"
|
||||||
26
SVM/Port.cs
Normal file
26
SVM/Port.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
abstract class Port
|
||||||
|
{
|
||||||
|
private VM vm;
|
||||||
|
|
||||||
|
public Port(VM vm)
|
||||||
|
{
|
||||||
|
this.vm = vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ushort Read();
|
||||||
|
public abstract void Write(byte val);
|
||||||
|
public virtual void Write(byte[] array)
|
||||||
|
{
|
||||||
|
foreach(var x in array)
|
||||||
|
{
|
||||||
|
Write(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
SVM/Ports/ConsolePort.cs
Normal file
24
SVM/Ports/ConsolePort.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM.Ports
|
||||||
|
{
|
||||||
|
class ConsolePort : Port
|
||||||
|
{
|
||||||
|
public ConsolePort(VM vm)
|
||||||
|
: base(vm)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public override ushort Read()
|
||||||
|
{
|
||||||
|
var result = Console.ReadKey(true);
|
||||||
|
return (byte)result.KeyChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte val)
|
||||||
|
{
|
||||||
|
Console.Write((char)val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SVM/Program.cs
Normal file
29
SVM/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(Environment.CurrentDirectory, args[0]);
|
||||||
|
|
||||||
|
var content = File.ReadAllText(file);
|
||||||
|
|
||||||
|
Console.WriteLine("Assembling {0}", file);
|
||||||
|
|
||||||
|
var asm = new Assembler();
|
||||||
|
|
||||||
|
var mem = asm.Compile(content);
|
||||||
|
|
||||||
|
var vm = new VM();
|
||||||
|
vm.CycleDelay = 25;
|
||||||
|
vm.Load(mem, 0);
|
||||||
|
|
||||||
|
vm.Run();
|
||||||
|
|
||||||
|
Console.ReadKey(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
SVM/Register.cs
Normal file
33
SVM/Register.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
static class Register
|
||||||
|
{
|
||||||
|
public static byte FromASM(string name)
|
||||||
|
{
|
||||||
|
switch(name.ToUpper())
|
||||||
|
{
|
||||||
|
case "A": return 0;
|
||||||
|
case "B": return 1;
|
||||||
|
case "C": return 2;
|
||||||
|
case "D": return 3;
|
||||||
|
default: throw new Exception("Unknown Register: "+name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToASM(byte code)
|
||||||
|
{
|
||||||
|
switch(code)
|
||||||
|
{
|
||||||
|
case 0: return "A";
|
||||||
|
case 1: return "B";
|
||||||
|
case 2: return "C";
|
||||||
|
case 3: return "D";
|
||||||
|
default: throw new Exception(string.Format("Unknown Register Code: {0}", code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SVM/SVM.csproj
Normal file
18
SVM/SVM.csproj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="PGM\A.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="PGM\A.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
83
SVM/VM.cs
Normal file
83
SVM/VM.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace SVM
|
||||||
|
{
|
||||||
|
class VM
|
||||||
|
{
|
||||||
|
public const int REGISTERS = 4;
|
||||||
|
public const int PORTS = 8;
|
||||||
|
public const int STACKDEPTH = 16;
|
||||||
|
public const int MEMSIZE = 0xFFFF;
|
||||||
|
|
||||||
|
public bool RUN;
|
||||||
|
public ushort PC;
|
||||||
|
public ushort[] R = new ushort[REGISTERS];
|
||||||
|
public ushort RI;
|
||||||
|
public byte SP;
|
||||||
|
public ushort[] STACK = new ushort[STACKDEPTH];
|
||||||
|
public byte[] MEM = new byte[MEMSIZE];
|
||||||
|
public Port[] Ports = new Port[PORTS];
|
||||||
|
|
||||||
|
public int CycleDelay = 0;
|
||||||
|
|
||||||
|
private Dictionary<byte, Instruction> instructions = new Dictionary<byte, Instruction>();
|
||||||
|
|
||||||
|
public VM()
|
||||||
|
{
|
||||||
|
var instrs = Instruction.GetAllInstructions();
|
||||||
|
foreach(var instr in instrs)
|
||||||
|
{
|
||||||
|
instructions.Add(instr.OP, instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ports[0] = new Ports.ConsolePort(this);
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
PC = 0;
|
||||||
|
SP = 0;
|
||||||
|
RI = 0;
|
||||||
|
Array.Fill<ushort>(R, 0);
|
||||||
|
Array.Fill<ushort>(STACK, 0);
|
||||||
|
Array.Fill<byte>(MEM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(byte[] data, byte origin)
|
||||||
|
{
|
||||||
|
Array.Copy(data, 0, MEM, origin, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
RUN = true;
|
||||||
|
while(RUN)
|
||||||
|
{
|
||||||
|
Step();
|
||||||
|
if (CycleDelay > 0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(CycleDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Step()
|
||||||
|
{
|
||||||
|
if (RUN)
|
||||||
|
{
|
||||||
|
//Get op
|
||||||
|
var pc = PC;
|
||||||
|
var instr = instructions[MEM[PC++]];
|
||||||
|
byte[] decoded = instr.Decode(this);
|
||||||
|
Console.WriteLine("A{0} B{1} C{2} D{3}", R[0], R[1], R[2], R[3]);
|
||||||
|
Console.WriteLine("0x{0:X4} {1}", pc, instr.ToASM(decoded));
|
||||||
|
instr.Exec(this, decoded);
|
||||||
|
Console.ReadKey(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user