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.

862 lines
36KB

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