using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Sloth
{
public class AssemblerException : InterpreterException
{
public AssemblerException(String message, Int32 lineNumber) : base(message, lineNumber) { }
}
public struct CompiledOperand
{
public AddressingModes Mode { get; set; }
public Int32 Value { get; set; }
public void SetRegister(String registerName)
{
this.Value |= GetRegisterIndex(registerName);
}
public void SetBaseRegister(String registerName)
{
this.Value |= GetRegisterIndex(registerName) << 8;
}
public void SetOffset(Int16 offset)
{
this.Value |= offset << 16;
}
public String GetRegister()
{
return GetRegisterName((Byte)(this.Value & 0xFF));
}
public String GetBaseRegister()
{
return GetRegisterName((Byte)((this.Value & 0xFF00) >> 8));
}
public Int16 GetOffset()
{
return (Int16)((this.Value & 0xFFFF0000) >> 16);
}
private static Byte GetRegisterIndex(String registerName)
{
return (Byte)((registerName[0] == 'A' ? 8 : 0) + Byte.Parse(registerName[1].ToString()));
}
private static String GetRegisterName(Byte index)
{
if (index >= 8)
return 'A' + (index - 8).ToString();
return 'D' + index.ToString();
}
}
public struct CompiledInstruction
{
private static readonly Int16
[] branchOpCodes
= (new String[] { "BRA",
"BLE",
"BGE",
"BLT",
"BGT",
"BVS",
"BVC",
"BCS",
"BCC",
"BEQ",
"BNE",
"BSR" }).
Select(name
=> name.
GetHashCode16()).
ToArray();
private Byte binarySize;
public Int16 OpCode { get; set; }
public List<CompiledOperand> Operands { get; set; }
public Byte Size { get; set; }
public Byte BinarySize
{
get
{
if (binarySize == 0)
binarySize = this.GetBinarySize();
return binarySize;
}
}
public void WriteBinary(BinaryWriter writer)
{
writer.Write(this.OpCode);
if (branchOpCodes.Contains(this.OpCode))
return;
foreach (var operand in this.Operands)
if (operand.Mode.HasAnyFlags(AddressingModes.Immediate, AddressingModes.Absolute, AddressingModes.ImmediateLabel, AddressingModes.AbsoluteLabel))
if (!operand.Value.IsInt16())
writer.Write(operand.Value);
else
writer.Write((Int16)operand.Value);
else if (operand.Mode.HasFlag(AddressingModes.Offset))
writer.Write((Int16)operand.Value);
}
private Byte GetBinarySize()
{
if (branchOpCodes.Contains(this.OpCode))
return 2;
Byte size = 2;
foreach(var operand in this.Operands)
if (operand.Mode.HasAnyFlags(AddressingModes.Immediate, AddressingModes.Absolute, AddressingModes.ImmediateLabel, AddressingModes.AbsoluteLabel))
if (!operand.Value.IsInt16())
size += 4;
else
size += 2;
else if (operand.Mode.HasFlag(AddressingModes.Offset))
size += 2;
return size;
}
}
public class AssemblyProgram
{
public MemoryStream Binary { get; private set; }
public Dictionary<Int32, CompiledInstruction> Instructions { get; private set; }
public AssemblyProgram()
{
this.
Binary = new MemoryStream
();
this.
Instructions = new Dictionary
<Int32, CompiledInstruction
>();
}
}
public class Assembler
{
private Dictionary<String, Int32> labels;
public AssemblyProgram Output { get; private set; }
public Assembler()
{
labels
= new Dictionary
<string,
int>();
}
public void Assemble(InstructionList instructions)
{
this.
Output = new AssemblyProgram
();
labels.Clear();
BinaryWriter writer
= new BinaryWriter
(this.
Output.
Binary);
Int32 position = 0;
foreach (Instruction instruction in instructions)
if (instruction.IsLabel)
labels.Add(instruction.Name.TrimEnd(':'), position);
else
position += instruction.GetBinarySize();
foreach (Instruction instruction in instructions)
{
if (instruction.IsLabel)
continue;
switch (instruction.Name)
{
case "DC":
for (Int32 i = 0; i < instruction.Operands.Count; i++)
{
if (instruction.Size == 32)
writer.Write(instruction.Operands[i].LiteralValue);
else if (instruction.Size == 16)
writer.Write((Int16)instruction.Operands[i].LiteralValue);
else
writer.Write((Byte)instruction.Operands[i].LiteralValue);
}
break;
case "DS":
for (Int32 i = 0; i < instruction.Size / 8 * instruction.Operands[0].LiteralValue; i++)
writer.Write((Byte)0);
break;
default:
CompiledInstruction compiled = this.CompileInstruction(instruction);
this.Output.Instructions.Add((Int32)this.Output.Binary.Position, compiled);
compiled.WriteBinary(writer);
break;
}
}
labels.Clear();
}
private CompiledInstruction CompileInstruction(Instruction instruction)
{
CompiledInstruction result
= new CompiledInstruction
();
result.OpCode = instruction.Name.GetHashCode16();
result.Size = instruction.Size;
try
{
result.Operands = instruction.Operands.Select(op => this.CompileOperand(op)).ToList();
}
catch
{
throw new AssemblerException
("L'etichetta specificata non è valida.", instruction.
LineNumber);
}
return result;
}
private CompiledOperand CompileOperand(Operand operand)
{
CompiledOperand result
= new CompiledOperand
();
result.Mode = operand.Mode;
if (operand.Mode.HasAnyFlags(AddressingModes.Absolute, AddressingModes.Immediate))
result.Value = operand.LiteralValue;
else if (operand.Mode.HasAnyFlags(AddressingModes.AbsoluteLabel, AddressingModes.ImmediateLabel))
result.Value = labels[operand.MemoryLocation];
else if (operand.Mode.HasAnyFlags(AddressingModes.DirectRegister, AddressingModes.IndirectRegister))
result.SetRegister(operand.MemoryLocation);
if (operand.Mode.HasFlag(AddressingModes.Base))
result.SetBaseRegister(operand.BaseRegister);
if (operand.Mode.HasFlag(AddressingModes.Offset))
result.SetOffset((Int16)operand.LiteralValue);
return result;
}
}
}