The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

814 lines
35KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license/
  7. Permission to use, copy, modify, and/or distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  11. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  13. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  14. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  16. OF THIS SOFTWARE.
  17. -----------------------------------------------------------------------------
  18. To release a closed-source product which uses other parts of JUCE not
  19. licensed under the ISC terms, commercial licenses are available: visit
  20. www.juce.com for more information.
  21. ==============================================================================
  22. */
  23. namespace littlefoot
  24. {
  25. /*
  26. This file contains classes and definitions for executing a littlefoot
  27. bytecode program that was created with the littlefoot compiler.
  28. */
  29. #if ! (defined (LITTLEFOOT_DEBUG_TRACE) || RUNNING_ON_REAL_BLOCK_DEVICE)
  30. #define LITTLEFOOT_DEBUG_TRACE 0
  31. #endif
  32. #if ! (defined (LITTLEFOOT_DUMP_PROGRAM) || RUNNING_ON_REAL_BLOCK_DEVICE)
  33. #define LITTLEFOOT_DUMP_PROGRAM 0
  34. #endif
  35. using int8 = char;
  36. using uint8 = unsigned char;
  37. using int16 = short;
  38. using uint16 = unsigned short;
  39. using int32 = int;
  40. using uint32 = unsigned int;
  41. using FunctionID = int16;
  42. #define LITTLEFOOT_OPCODES(OP, OP_INT8, OP_INT16, OP_INT32) \
  43. OP (halt) \
  44. OP_INT16 (jump) \
  45. OP_INT16 (jumpIfTrue) \
  46. OP_INT16 (jumpIfFalse) \
  47. OP_INT16 (call) \
  48. OP_INT8 (retVoid) \
  49. OP_INT8 (retValue) \
  50. OP_INT16 (callNative) \
  51. OP (drop) \
  52. OP_INT8 (dropMultiple) \
  53. OP_INT8 (pushMultiple0) \
  54. OP (push0) \
  55. OP (push1) \
  56. OP_INT8 (push8) \
  57. OP_INT16 (push16) \
  58. OP_INT32 (push32) \
  59. OP (dup) \
  60. OP (dupOffset_01) \
  61. OP (dupOffset_02) \
  62. OP (dupOffset_03) \
  63. OP (dupOffset_04) \
  64. OP (dupOffset_05) \
  65. OP (dupOffset_06) \
  66. OP (dupOffset_07) \
  67. OP_INT8 (dupOffset) \
  68. OP_INT16 (dupOffset16) \
  69. OP_INT8 (dropToStack) \
  70. OP_INT16 (dropToStack16) \
  71. OP_INT16 (dupFromGlobal) \
  72. OP_INT16 (dropToGlobal) \
  73. OP (int32ToFloat) \
  74. OP (floatToInt32) \
  75. OP (add_int32) \
  76. OP (add_float) \
  77. OP (mul_int32) \
  78. OP (mul_float) \
  79. OP (sub_int32) \
  80. OP (sub_float) \
  81. OP (div_int32) \
  82. OP (div_float) \
  83. OP (mod_int32) \
  84. OP (bitwiseOr) \
  85. OP (bitwiseAnd) \
  86. OP (bitwiseXor) \
  87. OP (bitwiseNot) \
  88. OP (bitShiftLeft) \
  89. OP (bitShiftRight) \
  90. OP (logicalOr) \
  91. OP (logicalAnd) \
  92. OP (logicalNot) \
  93. OP (testZE_int32) \
  94. OP (testNZ_int32) \
  95. OP (testGT_int32) \
  96. OP (testGE_int32) \
  97. OP (testLT_int32) \
  98. OP (testLE_int32) \
  99. OP (testZE_float) \
  100. OP (testNZ_float) \
  101. OP (testGT_float) \
  102. OP (testGE_float) \
  103. OP (testLT_float) \
  104. OP (testLE_float) \
  105. OP (getHeapByte) \
  106. OP (getHeapInt) \
  107. OP (getHeapBits) \
  108. OP (setHeapByte) \
  109. OP (setHeapInt) \
  110. enum class OpCode : uint8
  111. {
  112. #define LITTLEFOOT_OP(name) name,
  113. LITTLEFOOT_OPCODES (LITTLEFOOT_OP, LITTLEFOOT_OP, LITTLEFOOT_OP, LITTLEFOOT_OP)
  114. #undef LITTLEFOOT_OP
  115. endOfOpcodes
  116. };
  117. /** Available value types */
  118. enum class Type : uint8
  119. {
  120. void_ = 'v',
  121. int_ = 'i',
  122. bool_ = 'b',
  123. float_ = 'f'
  124. };
  125. //==============================================================================
  126. /** Defines a native function that the program can call. */
  127. struct NativeFunction
  128. {
  129. using ImplementationFunction = int32 (*) (void*, const int32*);
  130. /** Creates a NativeFunction from its signature and an implementation function.
  131. The format of nameAndArgumentTypes is "name/[return type][arg1][arg2..]"
  132. So for example "int foobar (float, bool)" would be "foobar/ifb"
  133. */
  134. NativeFunction (const char* nameAndArgumentTypes, ImplementationFunction fn) noexcept
  135. : nameAndArguments (nameAndArgumentTypes), function (fn),
  136. functionID (createID (nameAndArgumentTypes)), returnType(), numArgs()
  137. {
  138. const int slash = indexOfSlash (nameAndArgumentTypes);
  139. if (slash > 0)
  140. {
  141. returnType = static_cast<Type> (nameAndArgumentTypes[slash + 1]);
  142. while (nameAndArgumentTypes[slash + 2 + numArgs] != 0)
  143. ++numArgs;
  144. }
  145. }
  146. const char* nameAndArguments; /**< This signature must have the form "name/[return type][arg1][arg2..]" */
  147. ImplementationFunction function; /**< A static function that will be called. */
  148. FunctionID functionID; /**< The ID is a hash of the name + arguments, but not the return type. */
  149. Type returnType; /**< The function's return type. */
  150. uint8 numArgs; /**< The number of arguments that the function takes. */
  151. /** Converts a function signature to its hashed ID. */
  152. static FunctionID createID (const char* nameAndArgTypes) noexcept
  153. {
  154. jassert (nameAndArgTypes != nullptr && nameAndArgTypes[0] != 0); // the name cannot be an empty string!
  155. int hash = 0, i = 0;
  156. const int slash = indexOfSlash (nameAndArgTypes);
  157. jassert (slash > 0); // The slash can't be the first character in this string!
  158. jassert (nameAndArgTypes[slash + 1] != 0); // The slash must be followed by a return type character
  159. jassert (juce::String (nameAndArgTypes).substring (0, slash).containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"));
  160. jassert (! juce::String ("0123456789").containsChar (nameAndArgTypes[0]));
  161. jassert (juce::String (nameAndArgTypes).substring (slash + 1).containsOnly ("vif"));
  162. jassert (juce::String (nameAndArgTypes).substring (slash + 2).containsOnly ("if")); // arguments must only be of these types
  163. for (; nameAndArgTypes[i] != 0; ++i)
  164. if (i != slash + 1)
  165. hash = hash * 31 + nameAndArgTypes[i];
  166. return static_cast<FunctionID> (hash + i);
  167. }
  168. private:
  169. static int indexOfSlash (const char* nameAndArgs) noexcept
  170. {
  171. for (int i = 0; nameAndArgs[i] != 0; ++i)
  172. if (nameAndArgs[i] == '/')
  173. return i;
  174. return -1;
  175. }
  176. };
  177. //==============================================================================
  178. /**
  179. A reference to a block of memory which contains a complete program.
  180. Data format:
  181. 2 bytes - program checksum
  182. 2 bytes - program size
  183. 2 bytes - num functions
  184. 2 bytes - num globals
  185. 2 bytes - amount of heap space needed (bytes)
  186. 2 bytes - ID of function 1
  187. 2 bytes - byte offset of function 1 code
  188. 2 bytes - ID of function 2
  189. 2 bytes - byte offset of function 2 code
  190. etc..
  191. ...function code...
  192. */
  193. struct Program
  194. {
  195. Program (const void* data, uint32 totalMemorySize) noexcept
  196. : programStart (static_cast<const uint8*> (data)), maxProgramSize (totalMemorySize)
  197. {
  198. jassert (data != nullptr);
  199. }
  200. uint16 getStoredChecksum() const noexcept
  201. {
  202. return (uint16) readInt16 (programStart);
  203. }
  204. uint16 calculateChecksum() const noexcept
  205. {
  206. auto size = getProgramSize();
  207. uint16 n = (uint16) size;
  208. for (uint32 i = 2; i < size; ++i)
  209. n += (n * 2) + programStart[i];
  210. return n;
  211. }
  212. bool checksumMatches() const noexcept
  213. {
  214. return calculateChecksum() == getStoredChecksum();
  215. }
  216. uint32 getProgramSize() const noexcept
  217. {
  218. auto size = (uint16) readInt16 (programStart + 2);
  219. return size < programHeaderSize ? programHeaderSize
  220. : (size > maxProgramSize ? maxProgramSize : size);
  221. }
  222. uint32 getNumFunctions() const noexcept
  223. {
  224. return (uint16) readInt16 (programStart + 4);
  225. }
  226. FunctionID getFunctionID (uint32 functionIndex) const noexcept
  227. {
  228. if (auto f = getFunctionEntry (functionIndex))
  229. return static_cast<FunctionID> (readInt16 (f));
  230. return {};
  231. }
  232. const uint8* getFunctionStartAddress (uint32 functionIndex) const noexcept
  233. {
  234. if (auto f = getFunctionEntry (functionIndex))
  235. {
  236. uint32 address = (uint16) readInt16 (f + sizeof (FunctionID));
  237. if (address < getProgramSize())
  238. return programStart + address;
  239. }
  240. return {};
  241. }
  242. const uint8* getFunctionEndAddress (uint32 functionIndex) const noexcept
  243. {
  244. return ++functionIndex >= getNumFunctions() ? programStart + getProgramSize()
  245. : getFunctionStartAddress (functionIndex);
  246. }
  247. /** Returns the number of global variables the program uses */
  248. uint16 getNumGlobals() const noexcept
  249. {
  250. return (uint16) readInt16 (programStart + 6);
  251. }
  252. /** Returns the number of bytes of heap space the program needs */
  253. uint16 getHeapSizeBytes() const noexcept
  254. {
  255. return (uint16) readInt16 (programStart + 8);
  256. }
  257. #if JUCE_DEBUG
  258. //==============================================================================
  259. /** Prints the assembly code for a given function. */
  260. void dumpFunctionDisassembly (juce::OutputStream& out, uint32 functionIndex) const
  261. {
  262. out << juce::newLine << "Function #" << (int) functionIndex
  263. << " (" << juce::String::toHexString (getFunctionID (functionIndex)) << ")" << juce::newLine;
  264. if (auto codeStart = getFunctionStartAddress (functionIndex))
  265. if (auto codeEnd = getFunctionEndAddress (functionIndex))
  266. for (auto prog = codeStart; prog < codeEnd;)
  267. out << getOpDisassembly (prog) << juce::newLine;
  268. }
  269. juce::String getOpDisassembly (const uint8*& prog) const
  270. {
  271. juce::String s;
  272. s << juce::String::toHexString ((int) (prog - programStart)).paddedLeft ('0', 4) << ": ";
  273. auto op = (OpCode) *prog++;
  274. switch (op)
  275. {
  276. #define LITTLEFOOT_OP(name) case OpCode::name: s << #name; break;
  277. #define LITTLEFOOT_OP_INT8(name) case OpCode::name: s << #name << " " << juce::String::toHexString ((int) *prog++).paddedLeft ('0', 2); break;
  278. #define LITTLEFOOT_OP_INT16(name) case OpCode::name: s << #name << " " << juce::String::toHexString ((int) readInt16 (prog)).paddedLeft ('0', 4); prog += 2; break;
  279. #define LITTLEFOOT_OP_INT32(name) case OpCode::name: s << #name << " " << juce::String::toHexString ((int) readInt32 (prog)).paddedLeft ('0', 8); prog += 4; break;
  280. LITTLEFOOT_OPCODES (LITTLEFOOT_OP, LITTLEFOOT_OP_INT8, LITTLEFOOT_OP_INT16, LITTLEFOOT_OP_INT32)
  281. #undef LITTLEFOOT_OP
  282. #undef LITTLEFOOT_OP_INT8
  283. #undef LITTLEFOOT_OP_INT16
  284. #undef LITTLEFOOT_OP_INT32
  285. default: s << "???"; break;
  286. }
  287. return s;
  288. }
  289. /** Calls dumpFunctionDisassembly() for all functions. */
  290. void dumpAllFunctions (juce::OutputStream& out) const
  291. {
  292. DBG ("Program size: " << (int) getProgramSize() << " bytes");
  293. for (uint32 i = 0; i < getNumFunctions(); ++i)
  294. dumpFunctionDisassembly (out, i);
  295. }
  296. #endif
  297. //==============================================================================
  298. static float intToFloat (int32 value) noexcept { float v; copyFloatMem (&v, &value); return v; }
  299. static int32 floatToInt (float value) noexcept { int32 v; copyFloatMem (&v, &value); return v; }
  300. static int16 readInt16 (const uint8* d) noexcept { return (int16) (d[0] + (((uint16) d[1]) << 8)); }
  301. static int32 readInt32 (const uint8* d) noexcept { return (int32) ((uint32) (uint16) readInt16 (d) + (((uint32) (uint16) readInt16 (d + 2)) << 16)); }
  302. static void writeInt16 (uint8* d, int16 v) noexcept { d[0] = (uint8) v; d[1] = (uint8) (v >> 8); }
  303. static void writeInt32 (uint8* d, int32 v) noexcept { d[0] = (uint8) v; d[1] = (uint8) (v >> 8); d[2] = (uint8) (v >> 16); d[3] = (uint8) (v >> 24); }
  304. //==============================================================================
  305. static constexpr uint32 programHeaderSize = 10;
  306. const uint8* programStart = 0;
  307. const uint32 maxProgramSize;
  308. private:
  309. const uint8* getFunctionEntry (uint32 index) const noexcept
  310. {
  311. auto offset = programHeaderSize + index * (sizeof (FunctionID) + sizeof (int16));
  312. return offset <= (uint32) (getProgramSize() - 4) ? programStart + offset : nullptr;
  313. }
  314. static void copyFloatMem (void* dest, const void* src) noexcept
  315. {
  316. for (int i = 0; i < 4; ++i)
  317. ((uint8*) dest)[i] = ((const uint8*) src)[i];
  318. }
  319. };
  320. //==============================================================================
  321. /**
  322. Loads a program, and lets the user execute its functions.
  323. The programAndHeapSpace is the number of bytes allocated for program + heap.
  324. stackAndGlobalsSpace is the size of the globals + stack area.
  325. Memory layout:
  326. Program code goes at address 0, followed by any shared data the program needs
  327. globals are at the top end of the buffer
  328. stack space stretches downwards from the start of the globals
  329. */
  330. template <int programAndHeapSpace, int stackAndGlobalsSpace>
  331. struct Runner
  332. {
  333. Runner() noexcept : program (allMemory, sizeof (allMemory)) { reset(); }
  334. /** Installs an array of native functions that the code can use.
  335. Note that this doesn't take ownership of any memory involved, so the caller mustn't pass any dangling pointers
  336. */
  337. void setNativeFunctions (const NativeFunction* functions, int numFunctions, void* userDataForCallback) noexcept
  338. {
  339. nativeFunctions = functions;
  340. numNativeFunctions = numFunctions;
  341. nativeFunctionCallbackContext = userDataForCallback;
  342. }
  343. /** Returns the number of native functions available. */
  344. int getNumNativeFunctions() const noexcept { return numNativeFunctions; }
  345. /** Returns one of the native functions available. The index must not be out of range. */
  346. const NativeFunction& getNativeFunction (int index) const noexcept { jassert (index >= 0 && index < numNativeFunctions); return nativeFunctions[index]; }
  347. /** Clears the memory state. */
  348. void reset() noexcept
  349. {
  350. for (uint32 i = 0; i < sizeof (allMemory); ++i)
  351. allMemory[i] = 0;
  352. }
  353. /** Return codes from a function call */
  354. enum class ErrorCode : uint8
  355. {
  356. ok = 0,
  357. executionTimedOut,
  358. unknownInstruction,
  359. stackOverflow,
  360. stackUnderflow,
  361. illegalAddress,
  362. divisionByZero,
  363. unknownFunction
  364. };
  365. /** Calls one of the functions in the program, by its textual signature. */
  366. ErrorCode callFunction (const char* functionSignature) noexcept
  367. {
  368. return FunctionExecutionContext (*this, functionSignature).run();
  369. }
  370. /** Calls one of the functions in the program, by its function ID. */
  371. ErrorCode callFunction (FunctionID function) noexcept
  372. {
  373. return FunctionExecutionContext (*this, function).run();
  374. }
  375. /** */
  376. static constexpr uint32 totalProgramAndHeapSpace = programAndHeapSpace;
  377. /** */
  378. static constexpr uint32 totalStackAndGlobalsSpace = stackAndGlobalsSpace;
  379. /** */
  380. static uint32 getMaximumProgramSize() noexcept { return programAndHeapSpace; }
  381. /** */
  382. uint8* getProgramAndDataStart() const noexcept { return const_cast<uint8*> (allMemory); }
  383. /** */
  384. uint8* getProgramAndDataEnd() const noexcept { return reinterpret_cast<uint8*> (stackStart); }
  385. /** */
  386. uint32 getProgramAndDataSize() const noexcept { return (uint32) (getProgramAndDataEnd() - getProgramAndDataStart()); }
  387. /** */
  388. uint8* getProgramHeapStart() const noexcept { return heapStart; }
  389. /** */
  390. uint8* getProgramHeapEnd() const noexcept { return getProgramAndDataEnd(); }
  391. /** */
  392. uint16 getProgramHeapSize() const noexcept { return heapSize; }
  393. /** */
  394. bool isProgramValid() const noexcept { return heapStart != nullptr; }
  395. /** */
  396. void setDataByte (uint32 index, uint8 value) noexcept
  397. {
  398. if (index < programAndHeapSpace)
  399. {
  400. if (index < program.getProgramSize())
  401. heapStart = nullptr; // force a re-initialise of the memory layout when the program changes
  402. getProgramAndDataStart()[index] = value;
  403. }
  404. }
  405. /** */
  406. void setHeapByte (uint32 index, uint8 value) noexcept
  407. {
  408. auto* addr = getProgramHeapStart() + index;
  409. if (addr < getProgramHeapEnd())
  410. *addr = value;
  411. }
  412. /** */
  413. uint8 getHeapByte (uint32 index) const noexcept
  414. {
  415. const auto* addr = getProgramHeapStart() + index;
  416. return addr < getProgramHeapEnd() ? *addr : 0;
  417. }
  418. /** */
  419. uint32 getHeapBits (uint32 startBit, uint32 numBits) const noexcept
  420. {
  421. if (startBit + numBits > 8 * getProgramHeapSize())
  422. {
  423. jassertfalse;
  424. return 0;
  425. }
  426. return readLittleEndianBitsInBuffer (getProgramHeapStart(), startBit, numBits);
  427. }
  428. /** */
  429. int32 setHeapInt (uint32 byteOffset, uint32 value) noexcept
  430. {
  431. if (byteOffset + 3 < getProgramHeapSize())
  432. Program::writeInt32 (getProgramHeapStart() + byteOffset, (int32) value);
  433. return 0;
  434. }
  435. /** */
  436. int32 getHeapInt (uint32 byteOffset) const noexcept
  437. {
  438. return byteOffset + 3 < getProgramHeapSize() ? Program::readInt32 (getProgramHeapStart() + byteOffset) : 0;
  439. }
  440. //==============================================================================
  441. /** */
  442. uint8 allMemory[((programAndHeapSpace + stackAndGlobalsSpace) + 3) & ~3];
  443. /** */
  444. Program program;
  445. //==============================================================================
  446. /**
  447. */
  448. struct FunctionExecutionContext
  449. {
  450. FunctionExecutionContext() noexcept : programCounter (nullptr) {}
  451. FunctionExecutionContext (const FunctionExecutionContext&) noexcept = default;
  452. FunctionExecutionContext& operator= (const FunctionExecutionContext&) noexcept = default;
  453. /** */
  454. FunctionExecutionContext (Runner& r, const char* functionSignature) noexcept
  455. : FunctionExecutionContext (r, NativeFunction::createID (functionSignature)) {}
  456. /** */
  457. FunctionExecutionContext (Runner& r, FunctionID function) noexcept
  458. : runner (&r.reinitialiseProgramLayoutIfProgramHasChanged()),
  459. programBase (r.program.programStart), heapStart (r.heapStart),
  460. stack (r.stackEnd), stackStart (r.stackStart), stackEnd (r.stackEnd),
  461. globals (r.globals), heapSize (r.heapSize),
  462. programSize (r.program.getProgramSize()),
  463. numGlobals (r.program.getNumGlobals())
  464. {
  465. if (r.heapStart != nullptr)
  466. {
  467. auto& prog = r.program;
  468. auto numFunctions = prog.getNumFunctions();
  469. for (uint32 i = 0; i < numFunctions; ++i)
  470. {
  471. if (prog.getFunctionID (i) == function)
  472. {
  473. programCounter = prog.getFunctionStartAddress (i);
  474. functionEnd = prog.getFunctionEndAddress (i);
  475. tos = *--stack = 0;
  476. return;
  477. }
  478. }
  479. }
  480. programCounter = nullptr;
  481. }
  482. /** */
  483. bool isValid() const noexcept
  484. {
  485. return programCounter != nullptr && runner->heapStart != nullptr;
  486. }
  487. /** */
  488. void reset() noexcept
  489. {
  490. programCounter = nullptr;
  491. }
  492. /** */
  493. template <typename... Args>
  494. void setArguments (Args... args) noexcept { pushArguments (args...); push0(); /* (dummy return address) */ }
  495. /** */
  496. template <typename TimeOutCheckFunction>
  497. ErrorCode run (TimeOutCheckFunction hasTimedOut) noexcept
  498. {
  499. if (! isValid())
  500. return ErrorCode::unknownFunction;
  501. error = ErrorCode::unknownInstruction;
  502. uint16 opsPerformed = 0;
  503. for (;;)
  504. {
  505. if (programCounter >= functionEnd)
  506. return error;
  507. if ((++opsPerformed & 63) == 0 && hasTimedOut())
  508. return ErrorCode::executionTimedOut;
  509. dumpDebugTrace();
  510. auto op = (OpCode) *programCounter++;
  511. #define LITTLEFOOT_PERFORM_OP(name) case OpCode::name: name(); break;
  512. #define LITTLEFOOT_PERFORM_OP_INT8(name) case OpCode::name: name ((int8) *programCounter++); break;
  513. #define LITTLEFOOT_PERFORM_OP_INT16(name) case OpCode::name: name (readProgram16()); break;
  514. #define LITTLEFOOT_PERFORM_OP_INT32(name) case OpCode::name: name (readProgram32()); break;
  515. switch (op)
  516. {
  517. LITTLEFOOT_OPCODES (LITTLEFOOT_PERFORM_OP, LITTLEFOOT_PERFORM_OP_INT8, LITTLEFOOT_PERFORM_OP_INT16, LITTLEFOOT_PERFORM_OP_INT32)
  518. default: setError (ErrorCode::unknownInstruction); break;
  519. }
  520. jassert (programCounter != nullptr);
  521. }
  522. }
  523. private:
  524. //==============================================================================
  525. Runner* runner;
  526. const uint8* programCounter;
  527. const uint8* functionEnd;
  528. const uint8* programBase;
  529. uint8* heapStart;
  530. int32* stack;
  531. int32* stackStart;
  532. int32* stackEnd;
  533. int32* globals;
  534. uint16 heapSize, programSize, numGlobals;
  535. int32 tos; // top of stack
  536. ErrorCode error;
  537. template <typename Type1, typename... Args> void pushArguments (Type1 arg1, Args... args) noexcept { pushArguments (args...); pushArguments (arg1); }
  538. void pushArguments (int32 arg1) noexcept { push32 (arg1); }
  539. void pushArguments (float arg1) noexcept { push32 (Program::floatToInt (arg1)); }
  540. int16 readProgram16() noexcept { auto v = Program::readInt16 (programCounter); programCounter += sizeof (int16); return v; }
  541. int32 readProgram32() noexcept { auto v = Program::readInt32 (programCounter); programCounter += sizeof (int32); return v; }
  542. void setError (ErrorCode e) noexcept { error = e; programCounter = functionEnd; jassert (error == ErrorCode::ok); }
  543. bool checkStackUnderflow() noexcept { if (stack <= stackEnd) return true; setError (ErrorCode::stackUnderflow); return false; }
  544. bool flushTopToStack() noexcept { if (--stack < stackStart) { setError (ErrorCode::stackOverflow); return false; } *stack = tos; return true; }
  545. using IntBinaryOp = int32 (int32, int32);
  546. using FloatBinaryOp = float (float, float);
  547. void binaryOp (IntBinaryOp f) noexcept { if (checkStackUnderflow()) tos = f (*stack++, tos); }
  548. void binaryOp (FloatBinaryOp f) noexcept { if (checkStackUnderflow()) tos = Program::floatToInt (f (Program::intToFloat (*stack++), Program::intToFloat (tos))); }
  549. void halt() noexcept { setError (ErrorCode::ok); }
  550. void jump (int16 addr) noexcept { if (((uint16) addr) >= programSize) return setError (ErrorCode::illegalAddress); programCounter = programBase + (uint16) addr; }
  551. void jumpIfTrue (int16 addr) noexcept { bool v = tos; drop(); if (v) jump (addr); }
  552. void jumpIfFalse (int16 addr) noexcept { bool v = tos; drop(); if (! v) jump (addr); }
  553. void call (int16 fnAddr) noexcept { if (flushTopToStack()) { tos = (int32) (programCounter - programBase); jump (fnAddr); } }
  554. void retVoid (int8 numArgs) noexcept { if (tos == 0) return setError (ErrorCode::ok); auto retAddr = (int16) tos; stack += (uint8) numArgs; if (checkStackUnderflow()) { tos = *stack++; jump (retAddr); } }
  555. void retValue (int8 numArgs) noexcept { auto retAddr = (int16) *stack++; if (retAddr == 0) return setError (ErrorCode::ok); stack += (uint8) numArgs; if (checkStackUnderflow()) jump (retAddr); }
  556. void drop() noexcept { if (checkStackUnderflow()) tos = *stack++; }
  557. void dropMultiple (int8 num) noexcept { if (num < 0) { stack -= num; checkStackUnderflow(); } else { stack += num - 1; drop(); }}
  558. void pushMultiple0 (int8 num) noexcept { if (stack - num <= stackStart) return setError (ErrorCode::stackOverflow); flushTopToStack(); for (int i = (uint8) num; --i > 0;) *--stack = 0; tos = 0; }
  559. void push0() noexcept { push32 (0); }
  560. void push1() noexcept { push32 (1); }
  561. void push8 (int8 value) noexcept { push32 (value); }
  562. void push16 (int16 value) noexcept { push32 (value); }
  563. void push32 (int32 value) noexcept { flushTopToStack(); tos = value; }
  564. void dup() noexcept { flushTopToStack(); }
  565. void dupOffset_01() noexcept { dupOffset16 (1); }
  566. void dupOffset_02() noexcept { dupOffset16 (2); }
  567. void dupOffset_03() noexcept { dupOffset16 (3); }
  568. void dupOffset_04() noexcept { dupOffset16 (4); }
  569. void dupOffset_05() noexcept { dupOffset16 (5); }
  570. void dupOffset_06() noexcept { dupOffset16 (6); }
  571. void dupOffset_07() noexcept { dupOffset16 (7); }
  572. void dupOffset (int8 offset) noexcept { dupOffset16 ((uint8) offset); }
  573. void dupOffset16 (int16 offset) noexcept { if (flushTopToStack()) { auto addr = stack + offset; if (addr < stackStart || addr >= stackEnd) return setError (ErrorCode::illegalAddress); tos = *addr; } }
  574. void dropToStack (int8 offset) noexcept { dropToStack16 ((uint8) offset); }
  575. void dropToStack16 (int16 offset) noexcept { auto addr = stack + offset; if (addr < stackStart || addr >= stackEnd) return setError (ErrorCode::illegalAddress); *addr = tos; drop(); }
  576. void dupFromGlobal (int16 index) noexcept { if (flushTopToStack()) { if (((uint16) index) >= numGlobals) return setError (ErrorCode::illegalAddress); tos = globals [(uint16) index]; } }
  577. void dropToGlobal (int16 index) noexcept { if (((uint16) index) >= numGlobals) return setError (ErrorCode::illegalAddress); globals [(uint16) index] = tos; drop(); }
  578. void int32ToFloat() noexcept { tos = Program::floatToInt (static_cast<float> (tos)); }
  579. void floatToInt32() noexcept { tos = static_cast<int32> (Program::intToFloat (tos)); }
  580. void add_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a + b; }); }
  581. void add_float() noexcept { binaryOp ([] (float a, float b) { return a + b; }); }
  582. void mul_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a * b; }); }
  583. void mul_float() noexcept { binaryOp ([] (float a, float b) { return a * b; }); }
  584. void sub_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a - b; }); }
  585. void sub_float() noexcept { binaryOp ([] (float a, float b) { return a - b; }); }
  586. void div_int32() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (int32 a, int32 b) { return a / b; }); }
  587. void div_float() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (float a, float b) { return a / b; }); }
  588. void mod_int32() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (int32 a, int32 b) { return a % b; }); }
  589. void bitwiseOr() noexcept { binaryOp ([] (int32 a, int32 b) { return a | b; }); }
  590. void bitwiseAnd() noexcept { binaryOp ([] (int32 a, int32 b) { return a & b; }); }
  591. void bitwiseXor() noexcept { binaryOp ([] (int32 a, int32 b) { return a ^ b; }); }
  592. void bitShiftLeft() noexcept { binaryOp ([] (int32 a, int32 b) { return a << b; }); }
  593. void bitShiftRight() noexcept { binaryOp ([] (int32 a, int32 b) { return a >> b; }); }
  594. void logicalOr() noexcept { binaryOp ([] (int32 a, int32 b) { return (int32) (a || b); }); }
  595. void logicalAnd() noexcept { binaryOp ([] (int32 a, int32 b) { return (int32) (a && b); }); }
  596. void logicalNot() noexcept { tos = ! tos; }
  597. void bitwiseNot() noexcept { tos = ~tos; }
  598. void testZE_int32() noexcept { tos = (tos == 0); }
  599. void testNZ_int32() noexcept { tos = (tos != 0); }
  600. void testGT_int32() noexcept { tos = (tos > 0); }
  601. void testGE_int32() noexcept { tos = (tos >= 0); }
  602. void testLT_int32() noexcept { tos = (tos < 0); }
  603. void testLE_int32() noexcept { tos = (tos <= 0); }
  604. void testZE_float() noexcept { tos = (Program::intToFloat (tos) == 0.0f); }
  605. void testNZ_float() noexcept { tos = (Program::intToFloat (tos) != 0.0f); }
  606. void testGT_float() noexcept { tos = (Program::intToFloat (tos) > 0.0f); }
  607. void testGE_float() noexcept { tos = (Program::intToFloat (tos) >= 0.0f); }
  608. void testLT_float() noexcept { tos = (Program::intToFloat (tos) < 0.0f); }
  609. void testLE_float() noexcept { tos = (Program::intToFloat (tos) <= 0.0f); }
  610. void getHeapByte() noexcept { tos = runner->getHeapByte ((uint32) tos); }
  611. void getHeapInt() noexcept { tos = runner->getHeapInt ((uint32) tos); }
  612. void getHeapBits() noexcept { if (checkStackUnderflow()) tos = runner->getHeapBits ((uint32) tos, (uint32) *stack++); }
  613. void setHeapByte() noexcept { if (checkStackUnderflow()) runner->setHeapByte ((uint32) tos, (uint8) *stack++); drop(); }
  614. void setHeapInt() noexcept { if (checkStackUnderflow()) runner->setHeapInt ((uint32) tos, (uint32) *stack++); drop(); }
  615. void callNative (FunctionID functionID) noexcept
  616. {
  617. auto numFunctions = runner->numNativeFunctions;
  618. auto* functions = runner->nativeFunctions;
  619. for (int i = 0; i < numFunctions; ++i)
  620. {
  621. const auto& f = functions[i];
  622. if (f.functionID == functionID)
  623. {
  624. if (flushTopToStack())
  625. {
  626. tos = f.function (runner->nativeFunctionCallbackContext, stack);
  627. stack += f.numArgs;
  628. if (checkStackUnderflow() && f.returnType == Type::void_)
  629. drop();
  630. }
  631. return;
  632. }
  633. }
  634. setError (ErrorCode::unknownFunction);
  635. }
  636. void dumpDebugTrace() const
  637. {
  638. #if LITTLEFOOT_DEBUG_TRACE // Dumps the program counter and stack, for debugging
  639. juce::MemoryOutputStream dump;
  640. auto progCopy = programCounter;
  641. dump << juce::String (runner->program.getOpDisassembly (progCopy)).paddedRight (' ', 26)
  642. << juce::String::toHexString (tos) << ' ';
  643. for (auto s = stack; s < stackEnd; ++s)
  644. dump << juce::String::toHexString (*s) << ' ';
  645. DBG (dump.toString());
  646. #endif
  647. }
  648. };
  649. private:
  650. //==============================================================================
  651. const NativeFunction* nativeFunctions;
  652. int numNativeFunctions = 0;
  653. void* nativeFunctionCallbackContext = nullptr;
  654. uint8* heapStart = nullptr;
  655. int32* stackStart = nullptr;
  656. int32* stackEnd = nullptr;
  657. int32* globals = nullptr;
  658. uint16 heapSize = 0;
  659. Runner& reinitialiseProgramLayoutIfProgramHasChanged() noexcept
  660. {
  661. if (heapStart == nullptr && program.checksumMatches())
  662. {
  663. auto numGlobals = program.getNumGlobals();
  664. globals = reinterpret_cast<int32*> (allMemory + sizeof (allMemory)) - numGlobals;
  665. heapStart = getProgramAndDataStart() + program.getProgramSize();
  666. heapSize = program.getHeapSizeBytes();
  667. stackEnd = globals;
  668. stackStart = reinterpret_cast<int32*> (heapStart + heapSize);
  669. if ((uint8*) globals < heapStart || stackStart + 32 > stackEnd)
  670. {
  671. jassertfalse;
  672. heapStart = nullptr;
  673. }
  674. else
  675. {
  676. for (uint32 i = 0; i < numGlobals; ++i)
  677. globals[i] = 0; // clear globals
  678. #if LITTLEFOOT_DUMP_PROGRAM
  679. juce::MemoryOutputStream m;
  680. program.dumpAllFunctions (m);
  681. DBG (m.toString());
  682. #endif
  683. }
  684. }
  685. return *this;
  686. }
  687. };
  688. }