using SVM.Core.Data;
using SVM.Core.FuncImpl;
using SVM.Core.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using static SVM.Core.stdc.stdlib;
namespace SVM.Core
{
///
/// Memory Layout:
///
/// Index 0 - Program.
/// Index 1 - GPMemory:
/// Offset |Length |Usage
/// 0 |StackLength | Stack
///
public unsafe class SimpleVirtualMachine : IDisposable
{
public Registers registers;
public MemoryBlock Stack;
public List Memories = new List();
public SVMConfig? Config = null;
public MState MachineState;
public SVMProgram* Program = null;
public static uint InitGPMemorySize = 696320;
public Object? AdditionalData = null;
public ErrorIDs ErrorIDs = new ErrorIDs();
public DebugSymbol? symbols;
public void Init(uint StackSize = 1024 * 1024, uint RegisterSize = 512, uint GPMemory = uint.MaxValue)
{
registers.Init(RegisterSize);
uint CFOffset = 2;
uint SPOffset = 3;
if (Config != null)
{
CFOffset = Config.CFRegisterID;
}
if (GPMemory == 0)
{
}
else
{
if (GPMemory == uint.MaxValue)
{
GPMemory = InitGPMemorySize;
}
MemoryBlock block = new MemoryBlock();
block.Init(GPMemory);
SetMemory(1, block);
registers.SetData((int)CFOffset, new SVMPointer() { index = 1, offset = 0 });
var cfPtr = GetPointer(ReadCallFrameRegisterAsPtr());
cfPtr.SetData(ulong.MaxValue - 4);
registers.SetData((int)SPOffset, new SVMPointer() { index = 1, offset = StackSize });
}
}
public void Invoke(ulong PC)
{
while (true)
{
var ptr = ReadCallFrameRegisterAsPtr();
if (ptr.offset == 0) return;
Step();
}
}
public void Invoke(string funcName)
{
if (symbols != null)
{
if (symbols.Functions.TryGetValue(funcName, out var func))
{
Invoke(func);
}
}
}
public ulong GetPC()
{
uint PCOffset = 1;
if (Config != null)
{
PCOffset = Config.PCRegisterID;
}
return registers.GetData((int)PCOffset);
}
public bool isReachBinaryEnd()
{
uint PCOffset = 1;
if (Config != null)
{
PCOffset = Config.PCRegisterID;
}
var PC = registers.GetData((int)PCOffset);
return PC >= Program->InstructionCount;
}
public void WriteCallFrameRegister(ulong value)
{
uint CFOffset = 2;
if (Config != null)
{
CFOffset = Config.CFRegisterID;
}
registers.SetDataInRegister((int)CFOffset, value);
}
public ulong ReadCallFrameRegister()
{
uint CFOffset = 2;
if (Config != null)
{
CFOffset = Config.CFRegisterID;
}
return registers.GetData((int)CFOffset);
}
public SVMPointer ReadCallFrameRegisterAsPtr()
{
uint CFOffset = 2;
if (Config != null)
{
CFOffset = Config.CFRegisterID;
}
return registers.GetData((int)CFOffset);
}
public void Step()
{
uint CFOffset = 2;
uint PCOffset = 1;
uint ErrorIDOffset = 3;
if (Config != null)
{
CFOffset = Config.CFRegisterID;
PCOffset = Config.PCRegisterID;
ErrorIDOffset = Config.EIDRegisterID;
}
if (Program == null) return;
var PC = registers.GetData((int)PCOffset);
if (PC >= Program->InstructionCount) return;
var currentInstPtr = GetPointer(PC);
var Instruction = currentInstPtr.GetData();
var def = Instruction.GetDef();
fixed (MState* statePtr = &MachineState)
{
switch (def)
{
case PrimaryInstruction.BMath:
{
var Op = Instruction.GetData(1);
var NativeType = Instruction.GetData(2);
var L = Instruction.GetData(3);
var R = Instruction.GetData(4);
var T = Instruction.GetData(5);
switch (Op)
{
case BMathOp.Add:
MathImpl.MathAdd(registers, statePtr, NativeType, L, R, T, false);
break;
case BMathOp.Sub:
MathImpl.MathSub(registers, statePtr, NativeType, L, R, T, false);
break;
case BMathOp.Mul:
MathImpl.MathMul(registers, statePtr, NativeType, L, R, T, false);
break;
case BMathOp.Div:
MathImpl.MathDiv(registers, statePtr, NativeType, L, R, T, false);
break;
case BMathOp.Mod:
MathImpl.MathMod(registers, statePtr, NativeType, L, R, T);
break;
default:
break;
}
}
break;
case PrimaryInstruction.CBMath:
{
var Op = Instruction.GetData(1);
var NativeType = Instruction.GetData(2);
var L = Instruction.GetData(3);
var R = Instruction.GetData(4);
var T = Instruction.GetData(5);
switch (Op)
{
case BMathOp.Add:
MathImpl.MathAdd(registers, statePtr, NativeType, L, R, T, true);
break;
case BMathOp.Sub:
MathImpl.MathSub(registers, statePtr, NativeType, L, R, T, true);
break;
case BMathOp.Mul:
MathImpl.MathMul(registers, statePtr, NativeType, L, R, T, true);
break;
case BMathOp.Div:
MathImpl.MathDiv(registers, statePtr, NativeType, L, R, T, true);
break;
case BMathOp.Mod:
MathImpl.MathMod(registers, statePtr, NativeType, L, R, T);
break;
default:
break;
}
}
break;
case PrimaryInstruction.UMath:
break;
case PrimaryInstruction.Cvt:
{
Convert(Instruction);
}
break;
case PrimaryInstruction.Cmp:
{
var InstAlt = Instruction.CastAs(0);
var OP = (CompareOperator)InstAlt.D0;
switch (OP)
{
case CompareOperator.LT:
CompareFunctions.LT(this.registers, InstAlt);
break;
case CompareOperator.GT:
CompareFunctions.GT(this.registers, InstAlt);
break;
case CompareOperator.GE:
CompareFunctions.GE(this.registers, InstAlt);
break;
case CompareOperator.EQ:
CompareFunctions.EQ(this.registers, InstAlt);
break;
case CompareOperator.LE:
CompareFunctions.LE(this.registers, InstAlt);
break;
case CompareOperator.NE:
CompareFunctions.NE(this.registers, InstAlt);
break;
default:
break;
}
}
break;
case PrimaryInstruction.SD:
{
var Reg = Instruction.GetData(1);
PC++;
var dataPtr = GetPointer(PC);
var data = dataPtr.GetData();
registers.SetDataInRegister(Reg, data);
Console.WriteLine($"SVM:SD:{data} to {Reg} form PC={PC}");
}
break;
case PrimaryInstruction.JMP:
{
var RegisterID = Instruction.GetData(1);
PC = registers.ReadData(RegisterID);
Console.WriteLine($"Jump to:{PC}");
PC--;
}
break;
case PrimaryInstruction.JIF:
{
var RegisterID = Instruction.GetData(1);
var FlagID = Instruction.GetData(2);
bool isSet = false;
unsafe
{
switch (FlagID)
{
case ConditionFlag.CF:
isSet = statePtr->CF != 0;
break;
case ConditionFlag.Of:
isSet = statePtr->OF != 0;
break;
default:
break;
}
}
if (isSet)
{
PC = registers.ReadData(RegisterID);
PC--;
}
}
break;
case PrimaryInstruction.Load:
{
var Reg = Instruction.GetData(1);
var Length = Instruction.GetData(2);
var Target = Instruction.GetData(3);
var source = registers.GetData(Reg);
var srcPtr = GetPointer(source);
registers.SetDataInRegister(Target, srcPtr, Length);
}
break;
case PrimaryInstruction.Save:
{
var Reg = Instruction.GetData(1);
var Length = Instruction.GetData(2);
var Target = Instruction.GetData(3);
var tgtSVMPtr = registers.GetData(Target);
var tgtPtr = GetPointer(tgtSVMPtr);
var srcPtr = registers.GetPtr(Reg);
Buffer.MemoryCopy(srcPtr, (byte*)tgtPtr, Length, Length);
}
break;
case PrimaryInstruction.Call:
WriteCallFrameRegister(ReadCallFrameRegister() + 1);
{
var cfPtr = GetPointer(ReadCallFrameRegisterAsPtr());
cfPtr.SetData(PC + 1);
var RegisterID = Instruction.GetData(1);
PC = registers.ReadData(RegisterID);
Console.WriteLine($"Call to:{PC} ({RegisterID})");
PC--;
}
break;
case PrimaryInstruction.Return:
{
var cfPtr = GetPointer(ReadCallFrameRegisterAsPtr());
PC = cfPtr.GetData();
PC -= 1;
WriteCallFrameRegister(ReadCallFrameRegister() - 1);
}
break;
case PrimaryInstruction.System:
if (Config != null)
{
var target = Instruction.GetData(4);
if (Config.FuncCalls.TryGetValue(target, out var func))
{
func(this);
}
else
{
registers.SetData((int)ErrorIDOffset, ErrorIDs.SyscallCallNotExist);
}
}
else
{
registers.SetData((int)ErrorIDOffset, ErrorIDs.SyscallCallNotExist);
}
break;
case PrimaryInstruction.SIMD:
break;
default:
break;
}
}
PC++;
registers.SetData((int)PCOffset, PC);
//PC = registers.GetData((int)PCOffset);
}
private void Convert(SVMInstruction Instruction)
{
var SType = Instruction.GetData(1);
var TType = Instruction.GetData(2);
var L = Instruction.GetData(3);
var T = Instruction.GetData(4);
ICastable castable;
switch (SType)
{
case SVMNativeTypes.Int8:
castable = registers.GetData(L);
break;
case SVMNativeTypes.Int16:
castable = registers.GetData(L);
break;
case SVMNativeTypes.Int32:
castable = registers.GetData(L);
break;
case SVMNativeTypes.Int64:
castable = registers.GetData(L);
break;
case SVMNativeTypes.UInt8:
castable = registers.GetData(L);
break;
case SVMNativeTypes.UInt16:
castable = registers.GetData(L);
break;
case SVMNativeTypes.UInt32:
castable = registers.GetData(L);
break;
case SVMNativeTypes.UInt64:
castable = registers.GetData(L);
break;
case SVMNativeTypes.Float:
castable = registers.GetData(L);
break;
case SVMNativeTypes.Double:
castable = registers.GetData(L);
break;
default:
return;
}
switch (TType)
{
case SVMNativeTypes.Int8:
castable.Cast_SByte().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.Int16:
castable.Cast_Short().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.Int32:
castable.Cast_Int().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.Int64:
castable.Cast_Long().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.UInt8:
castable.Cast_Byte().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.UInt16:
castable.Cast_UShort().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.UInt32:
castable.Cast_UInt().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.UInt64:
castable.Cast_ULong().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.Float:
castable.Cast_Float().Write(registers.GetPtr(T));
break;
case SVMNativeTypes.Double:
castable.Cast_Double().Write(registers.GetPtr(T));
break;
default:
break;
}
}
public IntPtr GetPointer(ulong PC)
{
return GetPointer(new SVMPointer() { offset = (uint)(PC * (uint)sizeof(SVMInstruction)), index = 0 });
}
public IntPtr GetPointer(SVMPointer absoluteAddress)
{
if (absoluteAddress.index == 0)
{
ulong offset0 = 0;
ulong offset1 = 0;
if (Program != null)
{
offset0 = Program->InstructionCount * (ulong)sizeof(SVMInstruction);
offset1 = Program->DataSize;
if (absoluteAddress.offset < offset0)
{
return (IntPtr)(Program->instructions + absoluteAddress.offset / sizeof(SVMInstruction));
}
else if (absoluteAddress.offset < offset0 + offset1)
{
return IntPtr.Add((IntPtr)Program->data, (int)(absoluteAddress.offset - offset0));
}
}
}
var realIndex = absoluteAddress.index - 1;
if (realIndex < Memories.Count)
{
return IntPtr.Add(Memories[(int)realIndex].StartAddress, (int)absoluteAddress.offset);
}
return IntPtr.Zero;
}
public MemoryBlock SetMemory(int id, MemoryBlock block)
{
var realID = id - 1;
if (id < Memories.Count)
{
var old = Memories[(int)realID];
Memories[(int)realID] = block;
return old;
}
else
{
var count = realID - Memories.Count;
for (int i = 0; i <= count; i++)
{
Memories.Add(default);
}
Memories[(int)realID] = block;
return default;
}
}
public void Dispose()
{
registers.Dispose();
Stack.Dispose();
foreach (var item in Memories)
{
item.Dispose();
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SVMPointer
{
public uint offset;
public uint index;
public override string ToString()
{
return $"{index}+{offset}";
}
}
public class SVMConfig
{
public Dictionary FuncCalls = new Dictionary();
//Call Frame ID
public uint CFRegisterID;
public uint PCRegisterID;
public uint SPRegisterID;
///
/// Error ID Register.
///
public uint EIDRegisterID;
}
public class ErrorIDs
{
public int SyscallCallNotExist = 1;
public int MAX_ERROR_ID = short.MaxValue;
}
public delegate void FuncCall(SimpleVirtualMachine machine);
[StructLayout(LayoutKind.Sequential)]
public struct MState
{
public byte OF;
public byte CF;
}
[StructLayout(LayoutKind.Sequential)]
public struct MemoryBlock : IDisposable
{
public IntPtr StartAddress;
public uint Size;
public void Init(uint Size)
{
this.Size = Size;
StartAddress = malloc(Size);
}
public void Dispose()
{
free(StartAddress);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Callframe
{
public ulong PC;
public ulong SP;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SVMInstruction
{
public ulong data;
public T GetData(int offset) where T : unmanaged
{
fixed (ulong* dataPtr = &data)
{
return ((T*)(((byte*)dataPtr) + offset))[0];
}
}
public PrimaryInstruction GetDef()
{
fixed (ulong* dataPtr = &data)
{
return ((PrimaryInstruction*)dataPtr)[0];
}
}
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SVMInstruction_InSeparateBytes
{
public byte D0;
public byte D1;
public byte D2;
public byte D3;
public byte D4;
public byte D5;
public byte D6;
public byte D7;
}
}