Initial Commit

This commit is contained in:
2017-12-17 14:16:30 +00:00
parent 687e03b7c2
commit de38c1dab3
40 changed files with 1503 additions and 1 deletions

View File

@@ -1,2 +1,2 @@
# SVM
Sam Virtual Machine
Sams Virtual Machine

25
SVM.sln Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}
}
}
}