1 module navm.navm; 2 3 4 import std.conv : to; 5 6 import utils.ds; 7 import utils.misc; 8 9 public import navm.bytecode; 10 11 /// Stack, with no fixed data type for elements. 12 /// 13 /// Right now its not used anywhere, but it migth be useful. 14 public class NaStack{ 15 private: 16 ubyte[] _stack; 17 ubyte* _ptr; 18 ubyte* _lastPtr; 19 bool _errored; // if error occurred since last checked 20 public: 21 /// constructor 22 /// size is the size in bytes, default is 64 KiB 23 this(uinteger size = 1 << 16){ 24 _stack.length = size; 25 _lastPtr = _stack.ptr + size; 26 _ptr = _stack.ptr; 27 } 28 ~this(){ 29 .destroy(_stack); 30 } 31 /// if an error has occured, since this was last called 32 @property bool errored(){ 33 immutable bool r = _errored; 34 _errored = false; 35 return r; 36 } 37 /// seek index (number of bytes used on stack) 38 @property uinteger seek(){ 39 return _ptr - _stack.ptr; 40 } 41 /// empty stack, sets all bytes to zero, resets seek 42 void clear(){ 43 _stack[] = 0; 44 _ptr = _stack.ptr; 45 _errored = false; 46 } 47 /// pushes to stack 48 void push(T)(T element){ 49 if (_ptr + T.sizeof > _lastPtr){ 50 _errored = true; 51 return; 52 } 53 _ptr[0 .. T.sizeof] = (cast(ubyte*)&element)[0 .. T.sizeof]; 54 _ptr += T.sizeof; 55 } 56 /// pushes multiple elements to stack. last in array is first pushed 57 /// 58 /// Either all are pushed, or none if there isn't enough space 59 void pushArray(T)(T[] elements){ 60 immutable uinteger lenBytes = T.sizeof * elements.length; 61 if (_ptr + lenBytes > _lastPtr){ 62 _errored = true; 63 return; 64 } 65 _ptr[0 .. lenBytes] = (cast(ubyte*)elements.ptr)[0 .. lenBytes]; 66 _ptr += lenBytes; 67 } 68 /// pops from stack 69 /// 70 /// Sets `val` to popped value 71 void pop(T)(ref T val){ 72 if (_ptr < _stack.ptr + T.sizeof){ 73 _errored = true; 74 return; 75 } 76 _ptr -= T.sizeof; 77 val = *(cast(T*)_ptr); 78 } 79 /// pops multiple from stack. first popped is last in array 80 /// 81 /// Writes popped values to `vals`. If there arent enough elements on stack, will not pop anything 82 void popArray(T)(ref T[] vals){ 83 immutable uinteger lenBytes = T.sizeof * vals.length; 84 if (_ptr < _stack.ptr + lenBytes){ 85 _errored = true; 86 return; 87 } 88 _ptr -= lenBytes; 89 vals[0 .. vals.length] = (cast(T*)_ptr)[0 .. vals.length]; 90 } 91 } 92 /// 93 unittest{ 94 NaStack s = new NaStack(); 95 s.push!integer(integer.max); 96 s.push!ubyte(255); 97 s.push!byte(127); 98 byte b; 99 b = 0; 100 ubyte ub; 101 ub = 0; 102 integer[] iA = [0]; 103 assert(s.errored == false); 104 s.pop(b); 105 s.pop(ub); 106 s.pop(iA[0]); 107 assert(b == 127); 108 assert(ub == 255); 109 assert(iA[0] == integer.max); 110 iA = [integer.max, integer.max >> 1, 0, 1025]; 111 s.pushArray(iA); 112 iA[] = 0; 113 s.popArray(iA); 114 assert(iA == [integer.max, integer.max >> 1, 0, 1025]); 115 s.push(cast(integer)integer.max); 116 s.push(cast(integer)integer.max >> 1); 117 s.push(cast(integer)0); 118 s.push(cast(integer)1025); 119 iA[] = 0; 120 s.popArray(iA); 121 assert(iA == [integer.max, integer.max >> 1, 0, 1025]); 122 assert(s.errored == false); 123 .destroy(s); 124 } 125 126 /// NaVM abstract class 127 public abstract class NaVM{ 128 protected: 129 void delegate()[] _instructions; /// the instruction pointers 130 ubyte[] _args; /// stores arguments 131 uinteger _instIndex; /// index of next instruction 132 uinteger _argIndex; /// index next argument 133 uinteger[] _labelInstIndexes; /// instruction indexes for labels 134 uinteger[] _labelArgIndexes; /// argument indexes for labels 135 string[] _labelNames; /// label names 136 137 NaInstTable _instTable; /// instruction table 138 139 /// Gets an argument. **Do not use this when argument is array (string)** 140 T _readArg(T)(){ 141 immutable T r = *(cast(T*)(_args.ptr + _argIndex*(_argIndex + T.sizeof <= _args.length))); 142 _argIndex += T.sizeof; 143 return r; 144 } 145 /// ditto 146 T _readArg(T)(uinteger argAddr){ 147 return *(cast(T*)(_args.ptr + argAddr*(argAddr + T.sizeof <= _args.length))); 148 } 149 /// Reads an array from arguments. Will try to read enough bytes to fill `array` 150 void _readArgArray(T)(T[] array){ 151 immutable uinteger lenBytes = T.sizeof * array.length; 152 immutable uinteger altLenBytes = _args.length - _argIndex; 153 if (lenBytes > altLenBytes){ 154 (cast(ubyte*)array.ptr)[0 .. altLenBytes] = _args[_argIndex .. $]; 155 _argIndex = _args.length; 156 return; 157 } 158 (cast(ubyte*)array.ptr)[0 .. lenBytes] = _args[_argIndex .. _argIndex + lenBytes]; 159 _argIndex += lenBytes; 160 } 161 /// Changes value of an argument. **Do not use this when argument is array (string)** 162 /// 163 /// Returns: true if done, false if argument address is out of bounds 164 bool _writeArg(T)(uinteger argAddr, T val){ 165 if (argAddr + T.sizeof > _args.length) 166 return false; 167 *cast(T*)(_args.ptr + argAddr) = val; 168 return true; 169 } 170 public: 171 /// constructor 172 this(){ 173 _instTable = new NaInstTable(); 174 } 175 ~this(){ 176 .destroy(_instTable); 177 } 178 /// instruction table. 179 /// 180 /// **this will be destroyed when this class is destroyed** 181 @property NaInstTable instTable(){ 182 return _instTable; 183 } 184 /// label names, at corresponding label index 185 @property string[] labelNames(){ 186 return _labelNames; 187 } 188 /// loads bytecode 189 /// 190 /// Overriding: 191 /// this function must initialize `_instructions`, `_args`, `_argIndex`, 192 /// `_instIndex`, `_labelInstIndexes`, `_labelArgsIndexes`, and `_labelNames`. 193 /// 194 /// Returns: [] on success, or errors in case of any 195 string[] loadBytecode(NaBytecode code){ 196 if (!code.verify) 197 return ["bytecode.verify returned false"]; 198 _instIndex = 0; 199 _argIndex = 0; 200 string[] errors; 201 _instructions = code.instPtrs; 202 foreach (i, inst; _instructions){ 203 if (inst is null){ 204 try{ 205 NaInst instData = _instTable.getInstruction(code.instCodes[i]); 206 errors ~= "invalid pointer for instruction `"~instData.name~'`'; 207 }catch (Exception e){ 208 .destroy(e); 209 errors ~= "invalid instruction code "~code.instCodes[i].to!string; 210 } 211 } 212 } 213 // now labels 214 _labelNames = code.labelNames; 215 _labelInstIndexes.length = _labelNames.length; 216 _labelArgIndexes.length = _labelNames.length; 217 foreach (i, indexes; code.labelIndexes){ 218 _labelInstIndexes[i] = indexes[0]; 219 _labelArgIndexes[i] = indexes[1]; 220 } 221 // append arguments to _args 222 foreach (i, ref arg; code.instArgs){ 223 immutable NaInstArgType type = code.instArgTypes[i]; 224 if (type == NaInstArgType.String){ 225 ByteUnion!integer sizeStore; 226 string str = arg.value!string; 227 sizeStore.data = str.length; 228 _args ~= sizeStore.array ~ cast(ubyte[])str; 229 }else if (type == NaInstArgType.Boolean || type == NaInstArgType.Char){ 230 _args ~= arg.value!ubyte; 231 }else if (type == NaInstArgType.Double){ 232 ByteUnion!double valStore; 233 valStore.data = arg.value!double; 234 _args ~= valStore.array; 235 }else if (type == NaInstArgType.Integer || type == NaInstArgType.Label || 236 type == NaInstArgType.Address){ 237 ByteUnion!integer valStore; 238 valStore.data = arg.value!integer; 239 _args ~= valStore.array; 240 } 241 } 242 return errors; 243 } 244 /// starts execution from a label. Will do nothing if label invalid or doesnt exist 245 void execute(string labelName){ 246 integer index = _labelNames.indexOf(labelName); 247 if (index > -1) 248 execute(index); 249 } 250 /// ditto 251 void execute(uinteger labelIndex){ 252 if (labelIndex >= _labelInstIndexes.length) 253 return; 254 _argIndex = _labelArgIndexes[labelIndex]; 255 _instIndex = _labelInstIndexes[labelIndex]; 256 while (_instIndex < _instructions.length) 257 _instructions[_instIndex++](); 258 } 259 }