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