Files
SVM/SVM/Assembler.cs
Sam Stevens cf9004e206 Implemented faults and fault handling.
Added flags FLTSTS, FLTJH, FLTJL.

Added breakpoints and BRK instruction.

Added PUSH and POP instructions for interacting with the stack.
2017-12-31 23:18:17 +00:00

213 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Diagnostics;
using System.Text.RegularExpressions;
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()).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>>();
Dictionary<string, string> aliases = new Dictionary<string, string>();
//Encode ops until memory section
for (; i < lines.Length; i++)
{
line = lines[i];
if (line.ToUpper() == "MEMORY")
{
i++;
break;
}
//Remove multiple spaces
while (line.IndexOf(" ") != -1)
{
line = line.Replace(" ", " ");
}
//Replace aliases
foreach(var alias in aliases)
{
line = Regex.Replace(line, string.Format(@"(^|\b){0}($|\b)", alias.Key), alias.Value);
}
//Skip empty lines
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
//Records marker locations
if (line.StartsWith(':'))
{
var markerParts = line.Split(" ", 2);
markers.Add(markerParts[0].Substring(1), mempos);
if (markerParts.Length > 1 && !string.IsNullOrWhiteSpace(markerParts[1]))
{
line = markerParts[1].Trim();
}
else
{
continue;
}
}
parts = line.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
var op = parts[0].ToUpper();
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;
}
if (op == "ALIAS")
{
Debug.Assert(parts.Length == 2);
var aliasParts = parts[1].Split(' ', 2);
Debug.Assert(aliasParts.Length == 2);
if (aliases.ContainsKey(aliasParts[0]))
{
aliases.Remove(aliasParts[0]);
}
aliases.Add(aliasParts[0], aliasParts[1]);
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].ToUpper() : 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();
}
}
//Read MEMORY section
var dataEnd = lastpos;
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];
parts[1] = parts[1].Trim();
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);
}
}
if (dataOrigin < dataEnd)
{
throw new Exception("Memory section starts within data section");
}
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;
}
}
}