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; } }