diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0313a4117b8bb03fa7a10acf2a0a1bd591b246c..a02f6f8d8f1b287707842273b042448faed0223b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -247,8 +247,6 @@ if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) target_compile_definitions(SRB2SDL2 PRIVATE -DSRB2_CONFIG_ENABLE_WEBM_MOVIES) endif() -target_link_libraries(SRB2SDL2 PRIVATE acsvm) - set(SRB2_HAVE_THREADS ON) target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_THREADS) diff --git a/src/acs/CMakeLists.txt b/src/acs/CMakeLists.txt index e684675af0b4da61f5ad870b3bcf3f57108beb56..fda2e72f9da6f1689b2fc98f2490fe13b2e65c18 100644 --- a/src/acs/CMakeLists.txt +++ b/src/acs/CMakeLists.txt @@ -10,3 +10,11 @@ target_sources(SRB2SDL2 PRIVATE interface.cpp interface.h ) + +target_include_directories(SRB2SDL2 PRIVATE vm) # This sucks + +set(ACSVM_NOFLAGS ON) +set(ACSVM_SHARED OFF) +add_subdirectory(vm) + +target_link_libraries(SRB2SDL2 PRIVATE acsvm) diff --git a/src/acs/acsvm.hpp b/src/acs/acsvm.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4ae2ee62425a4b44fcc55b615fec68a830c07591 --- /dev/null +++ b/src/acs/acsvm.hpp @@ -0,0 +1,48 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2016 by James Haley, David Hill, et al. (Team Eternity) +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file acsvm.hpp +/// \brief ACSVM include file + +#ifndef __SRB2_ACSVM_HPP__ +#define __SRB2_ACSVM_HPP__ + +#include <ACSVM/Action.hpp> +#include <ACSVM/Array.hpp> +#include <ACSVM/BinaryIO.hpp> +#include <ACSVM/CallFunc.hpp> +#include <ACSVM/Code.hpp> +#include <ACSVM/CodeData.hpp> +#include <ACSVM/CodeList.hpp> +#include <ACSVM/Environment.hpp> +#include <ACSVM/Error.hpp> +#include <ACSVM/Function.hpp> +#include <ACSVM/HashMap.hpp> +#include <ACSVM/HashMapFixed.hpp> +#include <ACSVM/ID.hpp> +#include <ACSVM/Init.hpp> +#include <ACSVM/Jump.hpp> +#include <ACSVM/List.hpp> +#include <ACSVM/Module.hpp> +#include <ACSVM/PrintBuf.hpp> +#include <ACSVM/Scope.hpp> +#include <ACSVM/Script.hpp> +#include <ACSVM/Serial.hpp> +#include <ACSVM/Stack.hpp> +#include <ACSVM/Store.hpp> +#include <ACSVM/String.hpp> +#include <ACSVM/Thread.hpp> +#include <ACSVM/Tracer.hpp> +#include <ACSVM/Types.hpp> +#include <ACSVM/Vector.hpp> + +#include <Util/Floats.hpp> + +#endif //__SRB2_ACSVM_HPP__ diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index c0e91be7643031125fc111ba947ec4e90f835d48..4cbdcdeccfa0495c1b1d4b969fa3ca4147d59056 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -14,16 +14,7 @@ #include <algorithm> #include <cctype> -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" extern "C" { #include "../doomtype.h" diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index dff479bd35c6c5be7129903ba24b5315dbf62412..b5136832fd9747e345fd0f765b9456b8269ac337 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -14,16 +14,7 @@ #ifndef __SRB2_ACS_CALL_FUNCS_HPP__ #define __SRB2_ACS_CALL_FUNCS_HPP__ -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" /*-------------------------------------------------- bool CallFunc_???(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 38e74a17260a06e309941faa2f9b1fdcd8e37dc9..c51c222e11df8b012f139c0349e4fa8f38a0475d 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -14,16 +14,7 @@ #include <algorithm> #include <vector> -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" extern "C" { #include "../doomtype.h" diff --git a/src/acs/environment.hpp b/src/acs/environment.hpp index 5d080ad355098995c1f2eccd7e97b4eaf09cf656..fa6dbe03391054bf0c9d7c4a372ba413f18cdb47 100644 --- a/src/acs/environment.hpp +++ b/src/acs/environment.hpp @@ -14,16 +14,7 @@ #ifndef __SRB2_ACS_ENVIRONMENT_HPP__ #define __SRB2_ACS_ENVIRONMENT_HPP__ -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" namespace srb2::acs { diff --git a/src/acs/interface.cpp b/src/acs/interface.cpp index 864c1fa3f9a7b22754b3d7ea2c8af5a8f43a7b42..61b880961d5907a2995acd7afd1366fe7fbafd6c 100644 --- a/src/acs/interface.cpp +++ b/src/acs/interface.cpp @@ -16,17 +16,7 @@ #include <ostream> #include <vector> -#include <ACSVM/Action.hpp> -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" extern "C" { #include "interface.h" diff --git a/src/acs/stream.hpp b/src/acs/stream.hpp index ebe908e1d689fb640e950ca5b2f6ea760469560d..456a72532435916a3a1248216e64ab6d2d3333dc 100644 --- a/src/acs/stream.hpp +++ b/src/acs/stream.hpp @@ -19,16 +19,7 @@ #include <streambuf> -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" extern "C" { #include "../doomtype.h" diff --git a/src/acs/thread.cpp b/src/acs/thread.cpp index 5ebc7234ccacbdd999636f892d768c8d60dcf9d6..88494e5b30c38a9c1591ff55ec1221fc7d8cc732 100644 --- a/src/acs/thread.cpp +++ b/src/acs/thread.cpp @@ -11,17 +11,7 @@ /// \file thread.cpp /// \brief Action Code Script: Thread definition -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <ACSVM/BinaryIO.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" #include "thread.hpp" diff --git a/src/acs/thread.hpp b/src/acs/thread.hpp index eff31eb29a5f01170c87518bb7e411e02841f04a..5535ac556a6be39fc4466a95f7e1dd2c0448ccb8 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -14,16 +14,7 @@ #ifndef __SRB2_ACS_THREAD_HPP__ #define __SRB2_ACS_THREAD_HPP__ -#include <ACSVM/Code.hpp> -#include <ACSVM/CodeData.hpp> -#include <ACSVM/Environment.hpp> -#include <ACSVM/Error.hpp> -#include <ACSVM/Module.hpp> -#include <ACSVM/Scope.hpp> -#include <ACSVM/Script.hpp> -#include <ACSVM/Serial.hpp> -#include <ACSVM/Thread.hpp> -#include <Util/Floats.hpp> +#include "acsvm.hpp" extern "C" { #include "../doomtype.h" diff --git a/src/acs/vm/ACSVM/Action.cpp b/src/acs/vm/ACSVM/Action.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6aa857ba833c1373239a3f856bef4770feb0648 --- /dev/null +++ b/src/acs/vm/ACSVM/Action.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Deferred Action classes. +// +//----------------------------------------------------------------------------- + +#include "Action.hpp" + +#include "Environment.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // ScriptAction move constructor + // + ScriptAction::ScriptAction(ScriptAction &&a) : + action{std::move(a.action)}, + argV {std::move(a.argV)}, + id {std::move(a.id)}, + link {this, std::move(a.link)}, + name {std::move(a.name)} + { + } + + // + // ScriptAction constructor + // + ScriptAction::ScriptAction(ScopeID id_, ScriptName name_, Action action_, Vector<Word> &&argV_) : + action{action_}, + argV {std::move(argV_)}, + id {id_}, + link {this}, + name {name_} + { + } + + // + // ScriptAction destructor + // + ScriptAction::~ScriptAction() + { + } + + // + // ScriptAction::lockStrings + // + void ScriptAction::lockStrings(Environment *env) const + { + if(name.s) ++name.s->lock; + + for(auto &arg : argV) + ++env->getString(arg)->lock; + } + + // + // ScriptAction::refStrings + // + void ScriptAction::refStrings(Environment *env) const + { + if(name.s) name.s->ref = true; + + for(auto &arg : argV) + env->getString(arg)->ref = true; + } + + // + // ScriptAction::unlockStrings + // + void ScriptAction::unlockStrings(Environment *env) const + { + if(name.s) --name.s->lock; + + for(auto &arg : argV) + --env->getString(arg)->lock; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Action.hpp b/src/acs/vm/ACSVM/Action.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a2cef539418fd8e60c7dc8b98155da7b600382cc --- /dev/null +++ b/src/acs/vm/ACSVM/Action.hpp @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Deferred Action classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Action_H__ +#define ACSVM__Action_H__ + +#include "List.hpp" +#include "Script.hpp" +#include "Vector.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ScopeID + // + class ScopeID + { + public: + ScopeID() = default; + ScopeID(Word global_, Word hub_, Word map_) : + global{global_}, hub{hub_}, map{map_} {} + + bool operator == (ScopeID const &id) const + {return global == id.global && hub == id.hub && map == id.map;} + bool operator != (ScopeID const &id) const + {return global != id.global || hub != id.hub || map != id.map;} + + Word global; + Word hub; + Word map; + }; + + // + // ScriptAction + // + // Represents a deferred Script action. + // + class ScriptAction + { + public: + enum Action + { + Start, + StartForced, + Stop, + Pause, + }; + + + ScriptAction(ScriptAction &&action); + ScriptAction(ScopeID id, ScriptName name, Action action, Vector<Word> &&argV); + ~ScriptAction(); + + void lockStrings(Environment *env) const; + + void refStrings(Environment *env) const; + + void unlockStrings(Environment *env) const; + + Action action; + Vector<Word> argV; + ScopeID id; + ListLink<ScriptAction> link; + ScriptName name; + }; +} + +#endif//ACSVM__Action_H__ + diff --git a/src/acs/vm/ACSVM/Array.cpp b/src/acs/vm/ACSVM/Array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..510f847269482145f2d12f9d6107a83a20687196 --- /dev/null +++ b/src/acs/vm/ACSVM/Array.cpp @@ -0,0 +1,214 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Array class. +// +//----------------------------------------------------------------------------- + +#include "Array.hpp" + +#include "BinaryIO.hpp" +#include "Environment.hpp" +#include "Serial.hpp" + + +//----------------------------------------------------------------------------| +// Static Functions | +// + +namespace ACSVM +{ + // + // FreeData (Word) + // + static void FreeData(Word &) + { + } + + // + // FreeData + // + template<typename T> + static void FreeData(T *&data) + { + if(!data) return; + + for(auto &itr : *data) + FreeData(itr); + + delete[] data; + data = nullptr; + } + + // + // ReadData (Word) + // + static void ReadData(std::istream &in, Word &out) + { + out = ReadVLN<Word>(in); + } + + // + // ReadData + // + template<typename T> + static void ReadData(std::istream &in, T *&out) + { + if(in.get()) + { + if(!out) out = new T[1]{}; + + for(auto &itr : *out) + ReadData(in, itr); + } + else + FreeData(out); + } + + // + // RefStringsData (Word) + // + static void RefStringsData(Environment *env, Word const &data, void (*ref)(String *)) + { + ref(env->getString(data)); + } + + // + // RefStringsData + // + template<typename T> + static void RefStringsData(Environment *env, T *data, void (*ref)(String *)) + { + if(data) for(auto &itr : *data) + RefStringsData(env, itr, ref); + } + + // + // WriteData (Word) + // + static void WriteData(std::ostream &out, Word const &in) + { + WriteVLN(out, in); + } + + // + // WriteData + // + template<typename T> + static void WriteData(std::ostream &out, T *const &in) + { + if(in) + { + out.put('\1'); + + for(auto &itr : *in) + WriteData(out, itr); + } + else + out.put('\0'); + } +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Array::operator [Word] + // + Word &Array::operator [] (Word idx) + { + if(!data) data = new Data[1]{}; + Bank *&bank = (*data)[idx / (BankSize * SegmSize * PageSize)]; + + if(!bank) bank = new Bank[1]{}; + Segm *&segm = (*bank)[idx / (SegmSize * PageSize) % BankSize]; + + if(!segm) segm = new Segm[1]{}; + Page *&page = (*segm)[idx / PageSize % SegmSize]; + + if(!page) page = new Page[1]{}; + return (*page)[idx % PageSize]; + } + + // + // Array::find + // + Word Array::find(Word idx) const + { + if(!data) return 0; + Bank *&bank = (*data)[idx / (BankSize * SegmSize * PageSize)]; + + if(!bank) return 0; + Segm *&segm = (*bank)[idx / (SegmSize * PageSize) % BankSize]; + + if(!segm) return 0; + Page *&page = (*segm)[idx / PageSize % SegmSize]; + + if(!page) return 0; + return (*page)[idx % PageSize]; + } + + // + // Array::clear + // + void Array::clear() + { + FreeData(data); + } + + // + // Array::loadState + // + void Array::loadState(Serial &in) + { + in.readSign(Signature::Array); + ReadData(in, data); + in.readSign(~Signature::Array); + } + + // + // Array::lockStrings + // + void Array::lockStrings(Environment *env) const + { + RefStringsData(env, data, [](String *s){++s->lock;}); + } + + // + // Array::refStrings + // + void Array::refStrings(Environment *env) const + { + RefStringsData(env, data, [](String *s){s->ref = true;}); + } + + // + // Array::saveState + // + void Array::saveState(Serial &out) const + { + out.writeSign(Signature::Array); + WriteData(out, data); + out.writeSign(~Signature::Array); + } + + // + // Array::unlockStrings + // + void Array::unlockStrings(Environment *env) const + { + RefStringsData(env, data, [](String *s){--s->lock;}); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Array.hpp b/src/acs/vm/ACSVM/Array.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a71f3b58a0d79418a5f24db08fdf36a4808d6fae --- /dev/null +++ b/src/acs/vm/ACSVM/Array.hpp @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Array class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Array_H__ +#define ACSVM__Array_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Array + // + // Sparse-allocation array of 2**32 Words. + // + class Array + { + public: + Array() : data{nullptr} {} + Array(Array const &) = delete; + Array(Array &&array) : data{array.data} {array.data = nullptr;} + ~Array() {clear();} + + Word &operator [] (Word idx); + + void clear(); + + // If idx is allocated, returns that Word. Otherwise, returns 0. + Word find(Word idx) const; + + void loadState(Serial &in); + + void lockStrings(Environment *env) const; + + void refStrings(Environment *env) const; + + void saveState(Serial &out) const; + + void unlockStrings(Environment *env) const; + + private: + static constexpr std::size_t PageSize = 256; + static constexpr std::size_t SegmSize = 256; + static constexpr std::size_t BankSize = 256; + static constexpr std::size_t DataSize = 256; + + using Page = Word [PageSize]; + using Segm = Page*[SegmSize]; + using Bank = Segm*[BankSize]; + using Data = Bank*[DataSize]; + + Data *data; + }; +} + +#endif//ACSVM__Array_H__ + diff --git a/src/acs/vm/ACSVM/BinaryIO.cpp b/src/acs/vm/ACSVM/BinaryIO.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be229bad7550b1f13a30623421ced532412068c0 --- /dev/null +++ b/src/acs/vm/ACSVM/BinaryIO.cpp @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Binary data reading/writing primitives. +// +//----------------------------------------------------------------------------- + +#include "BinaryIO.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // ReadLE4 + // + std::uint_fast32_t ReadLE4(std::istream &in) + { + Byte buf[4]; + for(auto &b : buf) b = in.get(); + return ReadLE4(buf); + } + + // + // WriteLE4 + // + void WriteLE4(std::ostream &out, std::uint_fast32_t in) + { + Byte buf[4]; + WriteLE4(buf, in); + for(auto b : buf) out.put(b); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/BinaryIO.hpp b/src/acs/vm/ACSVM/BinaryIO.hpp new file mode 100644 index 0000000000000000000000000000000000000000..56aeea84b07d91c2b793733095c3748d466b859d --- /dev/null +++ b/src/acs/vm/ACSVM/BinaryIO.hpp @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Binary data reading/writing primitives. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__BinaryIO_H__ +#define ACSVM__BinaryIO_H__ + +#include "Types.hpp" + +#include <istream> +#include <ostream> +#include <climits> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + std::uint_fast8_t ReadLE1(Byte const *data); + std::uint_fast16_t ReadLE2(Byte const *data); + std::uint_fast32_t ReadLE4(Byte const *data); + std::uint_fast32_t ReadLE4(std::istream &in); + + template<typename T> + T ReadVLN(std::istream &in); + + void WriteLE4(Byte *out, std::uint_fast32_t in); + void WriteLE4(std::ostream &out, std::uint_fast32_t in); + + template<typename T> + void WriteVLN(std::ostream &out, T in); + + // + // ReadLE1 + // + inline std::uint_fast8_t ReadLE1(Byte const *data) + { + return static_cast<std::uint_fast8_t>(data[0]); + } + + // + // ReadLE2 + // + inline std::uint_fast16_t ReadLE2(Byte const *data) + { + return + (static_cast<std::uint_fast16_t>(data[0]) << 0) | + (static_cast<std::uint_fast16_t>(data[1]) << 8); + } + + // + // ReadLE4 + // + inline std::uint_fast32_t ReadLE4(Byte const *data) + { + return + (static_cast<std::uint_fast32_t>(data[0]) << 0) | + (static_cast<std::uint_fast32_t>(data[1]) << 8) | + (static_cast<std::uint_fast32_t>(data[2]) << 16) | + (static_cast<std::uint_fast32_t>(data[3]) << 24); + } + + // + // ReadVLN + // + template<typename T> + T ReadVLN(std::istream &in) + { + T out{0}; + + unsigned char c; + while(((c = in.get()) & 0x80) && in) + out = (out << 7) + (c & 0x7F); + out = (out << 7) + c; + + return out; + } + + // + // WriteLE4 + // + inline void WriteLE4(Byte *out, std::uint_fast32_t in) + { + out[0] = (in >> 0) & 0xFF; + out[1] = (in >> 8) & 0xFF; + out[2] = (in >> 16) & 0xFF; + out[3] = (in >> 24) & 0xFF; + } + + // + // WriteVLN + // + template<typename T> + void WriteVLN(std::ostream &out, T in) + { + constexpr std::size_t len = (sizeof(T) * CHAR_BIT + 6) / 7; + char buf[len], *ptr = buf + len; + + *--ptr = static_cast<char>(in & 0x7F); + while((in >>= 7)) + *--ptr = static_cast<char>(in & 0x7F) | 0x80; + + out.write(ptr, (buf + len) - ptr); + } +} + +#endif//ACSVM__BinaryIO_H__ + diff --git a/src/acs/vm/ACSVM/CMakeLists.txt b/src/acs/vm/ACSVM/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..29760733fb97ed9ff32d6a74f6c947f1a8500678 --- /dev/null +++ b/src/acs/vm/ACSVM/CMakeLists.txt @@ -0,0 +1,82 @@ +##----------------------------------------------------------------------------- +## +## Copyright (C) 2015-2017 David Hill +## +## See COPYING for license information. +## +##----------------------------------------------------------------------------- +## +## CMake file for acsvm. +## +##----------------------------------------------------------------------------- + + +##----------------------------------------------------------------------------| +## Environment Configuration | +## + +include_directories(.) + + +##----------------------------------------------------------------------------| +## Targets | +## + +## +## acsvm +## +add_library(acsvm ${ACSVM_SHARED_DECL} + Action.cpp + Action.hpp + Array.cpp + Array.hpp + BinaryIO.cpp + BinaryIO.hpp + CallFunc.cpp + CallFunc.hpp + Code.hpp + CodeData.cpp + CodeData.hpp + CodeList.hpp + Environment.cpp + Environment.hpp + Error.cpp + Error.hpp + Function.cpp + Function.hpp + HashMap.hpp + HashMapFixed.hpp + ID.hpp + Init.cpp + Init.hpp + Jump.cpp + Jump.hpp + Module.cpp + Module.hpp + ModuleACS0.cpp + ModuleACSE.cpp + PrintBuf.cpp + PrintBuf.hpp + Scope.cpp + Scope.hpp + Script.cpp + Script.hpp + Serial.cpp + Serial.hpp + Stack.hpp + Store.hpp + String.cpp + String.hpp + Thread.cpp + Thread.hpp + ThreadExec.cpp + Tracer.cpp + Tracer.hpp + Types.hpp + Vector.hpp +) + +ACSVM_INSTALL_LIB(acsvm) + +## EOF + diff --git a/src/acs/vm/ACSVM/CallFunc.cpp b/src/acs/vm/ACSVM/CallFunc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..142865e2da71c62b5f8c86985e20cc4e398ea288 --- /dev/null +++ b/src/acs/vm/ACSVM/CallFunc.cpp @@ -0,0 +1,480 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Internal CallFunc functions. +// +//----------------------------------------------------------------------------- + +#include "CallFunc.hpp" + +#include "Action.hpp" +#include "Code.hpp" +#include "Environment.hpp" +#include "Module.hpp" +#include "Scope.hpp" +#include "Thread.hpp" + +#include <cctype> +#include <cinttypes> + + +//----------------------------------------------------------------------------| +// Static Functions | +// + +namespace ACSVM +{ + // + // PrintArray + // + static void PrintArray(Thread *thread, Word const *argv, Word argc, Array const &arr) + { + Word idx = argv[0] + (argc > 2 ? argv[2] : 0); + Word len = argc > 3 ? argv[3] : -1; + + thread->env->printArray(thread->printBuf, arr, idx, len); + } + + // + // StrCaseCmp + // + static int StrCaseCmp(String *l, String *r, Word n) + { + for(char const *ls = l->str, *rs = r->str;; ++ls, ++rs) + { + char lc = std::toupper(*ls), rc = std::toupper(*rs); + if(lc != rc) return lc < rc ? -1 : 1; + if(!lc || !n--) return 0; + } + } + + // + // StrCmp + // + static int StrCmp(String *l, String *r, Word n) + { + for(char const *ls = l->str, *rs = r->str;; ++ls, ++rs) + { + char lc = *ls, rc = *rs; + if(lc != rc) return lc < rc ? -1 : 1; + if(!lc || !n--) return 0; + } + } + + // + // StrCpyArray + // + static bool StrCpyArray(Thread *thread, Word const *argv, Array &dst) + { + Word dstOff = argv[0] + argv[2]; + Word dstLen = argv[3]; + String *src = thread->scopeMap->getString(argv[4]); + Word srcIdx = argv[5]; + + if(srcIdx > src->len) return false; + + for(Word dstIdx = dstOff;;) + { + if(dstIdx - dstOff == dstLen) return false; + if(!(dst[dstIdx++] = src->str[srcIdx++])) return true; + } + } +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // void Nop() + // + bool CallFunc_Func_Nop(Thread *, Word const *, Word) + { + return false; + } + + // + // [[noreturn]] void Kill() + // + bool CallFunc_Func_Kill(Thread *thread, Word const *, Word) + { + thread->env->printKill(thread, static_cast<Word>(KillType::UnknownFunc), 0); + thread->state = ThreadState::Stopped; + return true; + } + + //====================================================== + // Printing Functions + // + + // + // void PrintChar(char c) + // + bool CallFunc_Func_PrintChar(Thread *thread, Word const *argv, Word) + { + thread->printBuf.reserve(1); + thread->printBuf.put(static_cast<char>(argv[0])); + return false; + } + + // + // str PrintEndStr() + // + bool CallFunc_Func_PrintEndStr(Thread *thread, Word const *, Word) + { + char const *data = thread->printBuf.data(); + std::size_t size = thread->printBuf.size(); + String *str = thread->env->getString(data, size); + thread->printBuf.drop(); + thread->dataStk.push(~str->idx); + return false; + } + + // + // void PrintFixD(fixed d) + // + bool CallFunc_Func_PrintFixD(Thread *thread, Word const *argv, Word) + { + // %E worst case: -3.276800e+04 == 13 + // %F worst case: -32767.999985 == 13 + // %G worst case: -1.52588e-05 == 12 + // %G should be maximally P+6 + extra exponent digits. + thread->printBuf.reserve(12); + thread->printBuf.format("%G", static_cast<std::int32_t>(argv[0]) / 65536.0); + return false; + } + + // + // void PrintGblArr(int idx, int arr, int off = 0, int len = -1) + // + bool CallFunc_Func_PrintGblArr(Thread *thread, Word const *argv, Word argc) + { + PrintArray(thread, argv, argc, thread->scopeGbl->arrV[argv[1]]); + return false; + } + + // + // void PrintHubArr(int idx, int arr, int off = 0, int len = -1) + // + bool CallFunc_Func_PrintHubArr(Thread *thread, Word const *argv, Word argc) + { + PrintArray(thread, argv, argc, thread->scopeHub->arrV[argv[1]]); + return false; + } + + // + // void PrintIntB(int b) + // + bool CallFunc_Func_PrintIntB(Thread *thread, Word const *argv, Word) + { + // %B worst case: 11111111111111111111111111111111 == 32 + char buf[32], *end = buf+32, *itr = end; + for(Word b = argv[0]; b; b >>= 1) *--itr = '0' + (b & 1); + thread->printBuf.reserve(end - itr); + thread->printBuf.put(itr, end - itr); + return false; + } + + // + // void PrintIntD(int d) + // + bool CallFunc_Func_PrintIntD(Thread *thread, Word const *argv, Word) + { + // %d worst case: -2147483648 == 11 + thread->printBuf.reserve(11); + thread->printBuf.format("%" PRId32, static_cast<std::int32_t>(argv[0])); + return false; + } + + // + // void PrintIntX(int x) + // + bool CallFunc_Func_PrintIntX(Thread *thread, Word const *argv, Word) + { + // %d worst case: FFFFFFFF == 8 + thread->printBuf.reserve(8); + thread->printBuf.format("%" PRIX32, static_cast<std::uint32_t>(argv[0])); + return false; + } + + // + // void PrintLocArr(int idx, int arr, int off = 0, int len = -1) + // + bool CallFunc_Func_PrintLocArr(Thread *thread, Word const *argv, Word argc) + { + PrintArray(thread, argv, argc, thread->localArr[argv[1]]); + return false; + } + + // + // void PrintModArr(int idx, int arr, int off = 0, int len = -1) + // + bool CallFunc_Func_PrintModArr(Thread *thread, Word const *argv, Word argc) + { + PrintArray(thread, argv, argc, *thread->scopeMod->arrV[argv[1]]); + return false; + } + + // + // void PrintPush() + // + bool CallFunc_Func_PrintPush(Thread *thread, Word const *, Word) + { + thread->printBuf.push(); + return false; + } + + // + // void PrintString(str s) + // + bool CallFunc_Func_PrintString(Thread *thread, Word const *argv, Word) + { + String *s = thread->scopeMap->getString(argv[0]); + thread->printBuf.reserve(s->len0); + thread->printBuf.put(s->str, s->len0); + return false; + } + + //====================================================== + // Script Functions + // + + // + // int ScrPauseS(str name, int map) + // + bool CallFunc_Func_ScrPauseS(Thread *thread, Word const *argV, Word) + { + String *name = thread->scopeMap->getString(argV[0]); + ScopeID scope{thread->scopeGbl->id, thread->scopeHub->id, argV[1]}; + if(!scope.map) scope.map = thread->scopeMap->id; + + thread->dataStk.push(thread->scopeMap->scriptPause(name, scope)); + return false; + } + + // + // int ScrStartS(str name, int map, ...) + // + bool CallFunc_Func_ScrStartS(Thread *thread, Word const *argV, Word argC) + { + String *name = thread->scopeMap->getString(argV[0]); + ScopeID scope{thread->scopeGbl->id, thread->scopeHub->id, argV[1]}; + if(!scope.map) scope.map = thread->scopeMap->id; + + thread->dataStk.push(thread->scopeMap->scriptStart(name, scope, {argV+2, argC-2})); + return false; + } + + // + // int ScrStartSD(str name, int map, int arg0, int arg1, int lock) + // + bool CallFunc_Func_ScrStartSD(Thread *thread, Word const *argV, Word) + { + if(!thread->env->checkLock(thread, argV[4], true)) + { + thread->dataStk.push(0); + return false; + } + + return CallFunc_Func_ScrStartS(thread, argV, 4); + } + + // + // int ScrStartSF(str name, int map, ...) + // + bool CallFunc_Func_ScrStartSF(Thread *thread, Word const *argV, Word argC) + { + String *name = thread->scopeMap->getString(argV[0]); + ScopeID scope{thread->scopeGbl->id, thread->scopeHub->id, argV[1]}; + if(!scope.map) scope.map = thread->scopeMap->id; + + thread->dataStk.push(thread->scopeMap->scriptStartForced(name, scope, {argV+2, argC-2})); + return false; + } + + // + // int ScrStartSL(str name, int map, int arg0, int arg1, int lock) + // + bool CallFunc_Func_ScrStartSL(Thread *thread, Word const *argV, Word) + { + if(!thread->env->checkLock(thread, argV[4], false)) + { + thread->dataStk.push(0); + return false; + } + + return CallFunc_Func_ScrStartS(thread, argV, 4); + } + + // + // int ScrStartSR(str name, ...) + // + bool CallFunc_Func_ScrStartSR(Thread *thread, Word const *argV, Word argC) + { + String *name = thread->scopeMap->getString(argV[0]); + + thread->dataStk.push(thread->scopeMap->scriptStartResult(name, {argV+1, argC-1})); + return false; + } + + // + // int ScrStopS(str name, int map) + // + bool CallFunc_Func_ScrStopS(Thread *thread, Word const *argV, Word) + { + String *name = thread->scopeMap->getString(argV[0]); + ScopeID scope{thread->scopeGbl->id, thread->scopeHub->id, argV[1]}; + if(!scope.map) scope.map = thread->scopeMap->id; + + thread->dataStk.push(thread->scopeMap->scriptStop(name, scope)); + return false; + } + + //====================================================== + // String Functions + // + + // + // int GetChar(str s, int i) + // + bool CallFunc_Func_GetChar(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(thread->scopeMap->getString(argv[0])->get(argv[1])); + return false; + } + + // + // int StrCaseCmp(str l, str r, int n = -1) + // + bool CallFunc_Func_StrCaseCmp(Thread *thread, Word const *argv, Word argc) + { + String *l = thread->scopeMap->getString(argv[0]); + String *r = thread->scopeMap->getString(argv[1]); + Word n = argc > 2 ? argv[2] : -1; + + thread->dataStk.push(StrCaseCmp(l, r, n)); + return false; + } + + // + // int StrCmp(str l, str r, int n = -1) + // + bool CallFunc_Func_StrCmp(Thread *thread, Word const *argv, Word argc) + { + String *l = thread->scopeMap->getString(argv[0]); + String *r = thread->scopeMap->getString(argv[1]); + Word n = argc > 2 ? argv[2] : -1; + + thread->dataStk.push(StrCmp(l, r, n)); + return false; + } + + // + // int StrCpyGblArr(int idx, int dst, int dstOff, int dstLen, str src, int srcOff) + // + bool CallFunc_Func_StrCpyGblArr(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(StrCpyArray(thread, argv, thread->scopeGbl->arrV[argv[1]])); + return false; + } + + // + // int StrCpyHubArr(int idx, int dst, int dstOff, int dstLen, str src, int srcOff) + // + bool CallFunc_Func_StrCpyHubArr(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(StrCpyArray(thread, argv, thread->scopeHub->arrV[argv[1]])); + return false; + } + + // + // int StrCpyLocArr(int idx, int dst, int dstOff, int dstLen, str src, int srcOff) + // + bool CallFunc_Func_StrCpyLocArr(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(StrCpyArray(thread, argv, thread->localArr[argv[1]])); + return false; + } + + // + // int StrCpyModArr(int idx, int dst, int dstOff, int dstLen, str src, int srcOff) + // + bool CallFunc_Func_StrCpyModArr(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(StrCpyArray(thread, argv, *thread->scopeMod->arrV[argv[1]])); + return false; + } + + // + // str StrLeft(str s, int len) + // + bool CallFunc_Func_StrLeft(Thread *thread, Word const *argv, Word) + { + String *str = thread->scopeMap->getString(argv[0]); + Word len = argv[1]; + + if(len < str->len) + str = thread->env->getString(str->str, len); + + thread->dataStk.push(~str->idx); + return false; + } + + // + // int StrLen(str s) + // + bool CallFunc_Func_StrLen(Thread *thread, Word const *argv, Word) + { + thread->dataStk.push(thread->scopeMap->getString(argv[0])->len0); + return false; + } + + // + // str StrMid(str s, int idx, int len) + // + bool CallFunc_Func_StrMid(Thread *thread, Word const *argv, Word) + { + String *str = thread->scopeMap->getString(argv[0]); + Word idx = argv[1]; + Word len = argv[2]; + + if(idx < str->len) + { + if(len < str->len - idx) + str = thread->env->getString(str->str + idx, len); + else + str = thread->env->getString(str->str + idx, str->len - idx); + } + else + str = thread->env->getString("", static_cast<std::size_t>(0)); + + thread->dataStk.push(~str->idx); + return false; + } + + // + // str StrRight(str s, int len) + // + bool CallFunc_Func_StrRight(Thread *thread, Word const *argv, Word) + { + String *str = thread->scopeMap->getString(argv[0]); + Word len = argv[1]; + + if(len < str->len) + str = thread->env->getString(str->str + str->len - len, len); + + thread->dataStk.push(~str->idx); + return false; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/CallFunc.hpp b/src/acs/vm/ACSVM/CallFunc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8813e85462a93546ae1f327d77df1b4e528160d4 --- /dev/null +++ b/src/acs/vm/ACSVM/CallFunc.hpp @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Internal CallFunc functions. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__CallFunc_H__ +#define ACSVM__CallFunc_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + #define ACSVM_FuncList(name) \ + bool (CallFunc_Func_##name)(Thread *thread, Word const *argv, Word argc); + #include "CodeList.hpp" +} + +#endif//ACSVM__CallFunc_H__ + diff --git a/src/acs/vm/ACSVM/Code.hpp b/src/acs/vm/ACSVM/Code.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a3b16cc7617eda1f06d8fbf272046bf7d1852082 --- /dev/null +++ b/src/acs/vm/ACSVM/Code.hpp @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Code classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Code_H__ +#define ACSVM__Code_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Code + // + // Internal codes. + // + enum class Code + { + #define ACSVM_CodeList(name, ...) name, + #include "CodeList.hpp" + + None + }; + + // + // CodeACS0 + // + // ACS0 codes. + // + enum class CodeACS0 + { + #define ACSVM_CodeListACS0(name, idx, ...) name = idx, + #include "CodeList.hpp" + + None + }; + + // + // Func + // + // Internal CallFunc indexes. + // + enum class Func + { + #define ACSVM_FuncList(name) name, + #include "CodeList.hpp" + + None + }; + + // + // FuncACS0 + // + // ACS0 CallFunc indexes. + // + enum class FuncACS0 + { + #define ACSVM_FuncListACS0(name, idx, ...) name = idx, + #include "CodeList.hpp" + + None + }; + + // + // KillType + // + enum class KillType + { + None, + OutOfBounds, + UnknownCode, + UnknownFunc, + BranchLimit, + }; +} + +#endif//ACSVM__Code_H__ + diff --git a/src/acs/vm/ACSVM/CodeData.cpp b/src/acs/vm/ACSVM/CodeData.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a05ed4ae07b657374cada59e5f93feb18204a42 --- /dev/null +++ b/src/acs/vm/ACSVM/CodeData.cpp @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// CodeData classes. +// +//----------------------------------------------------------------------------- + +#include "CodeData.hpp" + +#include "Code.hpp" + +#include <algorithm> +#include <cstring> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // CodeDataACS0 constructor + // + CodeDataACS0::CodeDataACS0(char const *args_, Code transCode_, + Word stackArgC_, Word transFunc_) : + code {CodeACS0::None}, + args {args_}, + argc {CountArgs(args_)}, + stackArgC{stackArgC_}, + transCode{transCode_}, + transFunc{transFunc_} + { + } + + // + // CodeDataACS0 constructor + // + CodeDataACS0::CodeDataACS0(char const *args_, Word stackArgC_, Word transFunc_) : + code {CodeACS0::None}, + args {args_}, + argc {CountArgs(args_)}, + stackArgC{stackArgC_}, + transCode{argc ? Code::CallFunc_Lit : Code::CallFunc}, + transFunc{transFunc_} + { + } + + // + // CodeDataACS0 constructor + // + CodeDataACS0::CodeDataACS0(CodeACS0 code_, char const *args_, + Code transCode_, Word stackArgC_, Func transFunc_) : + code {code_}, + args {args_}, + argc {CountArgs(args_)}, + stackArgC{stackArgC_}, + transCode{transCode_}, + transFunc{transFunc_ != Func::None ? static_cast<Word>(transFunc_) : 0} + { + } + + // + // CodeDataACS0::CountArgs + // + std::size_t CodeDataACS0::CountArgs(char const *args) + { + std::size_t argc = 0; + + for(; *args; ++args) switch(*args) + { + case 'B': + case 'H': + case 'W': + case 'b': + case 'h': + ++argc; + break; + } + + return argc; + } + + // + // FuncDataACS0 copy constructor + // + FuncDataACS0::FuncDataACS0(FuncDataACS0 const &data) : + transFunc{data.transFunc}, + + transCodeV{new TransCode[data.transCodeC]}, + transCodeC{data.transCodeC} + { + std::copy(data.transCodeV, data.transCodeV + transCodeC, transCodeV); + } + + // + // FuncDataACS0 move constructor + // + FuncDataACS0::FuncDataACS0(FuncDataACS0 &&data) : + transFunc{data.transFunc}, + + transCodeV{data.transCodeV}, + transCodeC{data.transCodeC} + { + data.transCodeV = nullptr; + data.transCodeC = 0; + } + + // + // FuncDataACS0 constructor + // + FuncDataACS0::FuncDataACS0(FuncACS0 func_, Func transFunc_, + std::initializer_list<TransCode> transCodes) : + func{func_}, + + transFunc{transFunc_ != Func::None ? static_cast<Word>(transFunc_) : 0}, + + transCodeV{new TransCode[transCodes.size()]}, + transCodeC{transCodes.size()} + { + std::copy(transCodes.begin(), transCodes.end(), transCodeV); + } + + // + // FuncDataACS0 constructor + // + FuncDataACS0::FuncDataACS0(Word transFunc_) : + func{FuncACS0::None}, + + transFunc{transFunc_}, + + transCodeV{nullptr}, + transCodeC{0} + { + } + + // + // FuncDataACS0 constructor + // + FuncDataACS0::FuncDataACS0(Word transFunc_, + std::initializer_list<TransCode> transCodes) : + func{FuncACS0::None}, + + transFunc{transFunc_}, + + transCodeV{new TransCode[transCodes.size()]}, + transCodeC{transCodes.size()} + { + std::copy(transCodes.begin(), transCodes.end(), transCodeV); + } + + // + // FuncDataACS0 constructor + // + FuncDataACS0::FuncDataACS0(Word transFunc_, + std::unique_ptr<TransCode[]> &&transCodeV_, std::size_t transCodeC_) : + func{FuncACS0::None}, + + transFunc{transFunc_}, + + transCodeV{transCodeV_.release()}, + transCodeC{transCodeC_} + { + } + + // + // FuncDataACS0 destructor + // + FuncDataACS0::~FuncDataACS0() + { + delete[] transCodeV; + } + + // + // FuncDataACS0::operator = FuncDataACS0 + // + FuncDataACS0 &FuncDataACS0::operator = (FuncDataACS0 &&data) + { + std::swap(transFunc, data.transFunc); + + std::swap(transCodeV, data.transCodeV); + std::swap(transCodeC, data.transCodeC); + + return *this; + } + + // + // FuncDataACS0::getTransCode + // + Code FuncDataACS0::getTransCode(Word argc) const + { + for(auto itr = transCodeV, end = itr + transCodeC; itr != end; ++itr) + if(itr->first == argc) return itr->second; + + return Code::CallFunc; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/CodeData.hpp b/src/acs/vm/ACSVM/CodeData.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71b6f635afeb61483496229d8a5baaf7c69a07ca --- /dev/null +++ b/src/acs/vm/ACSVM/CodeData.hpp @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// CodeData classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__CodeData_H__ +#define ACSVM__CodeData_H__ + +#include "Types.hpp" + +#include <initializer_list> +#include <memory> +#include <utility> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // CodeData + // + // Internal code description. + // + class CodeData + { + public: + Code code; + + Word argc; + }; + + // + // CodeDataACS0 + // + // ACS0 code description. + // + class CodeDataACS0 + { + public: + CodeDataACS0(char const *args, Code transCode, Word stackArgC, + Word transFunc = 0); + CodeDataACS0(char const *args, Word stackArgC, Word transFunc); + CodeDataACS0(CodeACS0 code, char const *args, Code transCode, + Word stackArgC, Func transFunc); + + // Code index. If not an internally recognized code, is set to None. + CodeACS0 code; + + // String describing the code's arguments. + // A - Previous value is MapReg index. + // a - Previous Value is MapArr index. + // B - Single byte. + // b - Single byte if compressed, full word otherwise. + // G - Previous value is GblReg index. + // g - Previous Value is GblArr index. + // H - Half word. + // h - Half word if compressed, full word otherwise. + // J - Previous value is jump index. + // L - Previous value is LocReg index. + // l - Previous Value is LocArr index. + // O - Previous value is ModReg index. + // o - Previous Value is ModArr index. + // S - Previous value is string index. + // U - Previous value is HubReg index. + // u - Previous Value is HubArr index. + // W - Full word. + char const *args; + std::size_t argc; + + // Stack argument count. + Word stackArgC; + + // Internal code to translate to. + Code transCode; + + // CallFunc index to translate to. + Word transFunc; + + private: + static std::size_t CountArgs(char const *args); + }; + + // + // FuncDataACS0 + // + // ACS0 CallFunc description. + // + class FuncDataACS0 + { + public: + using TransCode = std::pair<Word, Code>; + + FuncDataACS0(FuncDataACS0 const &); + FuncDataACS0(FuncDataACS0 &&data); + FuncDataACS0(FuncACS0 func, Func transFunc, + std::initializer_list<TransCode> transCodes); + FuncDataACS0(Word transFunc); + FuncDataACS0(Word transFunc, std::initializer_list<TransCode> transCodes); + FuncDataACS0(Word transFunc, std::unique_ptr<TransCode[]> &&transCodeV, + std::size_t transCodeC); + ~FuncDataACS0(); + + FuncDataACS0 &operator = (FuncDataACS0 const &) = delete; + FuncDataACS0 &operator = (FuncDataACS0 &&data); + + // Internal code to translate to. + Code getTransCode(Word argc) const; + + // CallFunc index. If not internally recognized, is set to None. + FuncACS0 func; + + // CallFunc index to translate to. + Word transFunc; + + private: + TransCode *transCodeV; + std::size_t transCodeC; + }; +} + +#endif//ACSVM__CodeData_H__ + diff --git a/src/acs/vm/ACSVM/CodeList.hpp b/src/acs/vm/ACSVM/CodeList.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4a4635c36e616b9c520cd57f40f97853f51c24e8 --- /dev/null +++ b/src/acs/vm/ACSVM/CodeList.hpp @@ -0,0 +1,454 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// List of all codes. +// +//----------------------------------------------------------------------------- + + +#ifdef ACSVM_CodeList + +ACSVM_CodeList(Nop, 0) +ACSVM_CodeList(Kill, 2) + +// Binary operator codes. +#define ACSVM_CodeList_BinaryOpSet(name) \ + ACSVM_CodeList(name, 0) \ + ACSVM_CodeList(name##_GblArr, 1) \ + ACSVM_CodeList(name##_GblReg, 1) \ + ACSVM_CodeList(name##_HubArr, 1) \ + ACSVM_CodeList(name##_HubReg, 1) \ + ACSVM_CodeList(name##_LocArr, 1) \ + ACSVM_CodeList(name##_LocReg, 1) \ + ACSVM_CodeList(name##_ModArr, 1) \ + ACSVM_CodeList(name##_ModReg, 1) +ACSVM_CodeList_BinaryOpSet(AddU) +ACSVM_CodeList_BinaryOpSet(AndU) +ACSVM_CodeList_BinaryOpSet(DivI) +ACSVM_CodeList_BinaryOpSet(ModI) +ACSVM_CodeList_BinaryOpSet(MulU) +ACSVM_CodeList_BinaryOpSet(OrIU) +ACSVM_CodeList_BinaryOpSet(OrXU) +ACSVM_CodeList_BinaryOpSet(ShLU) +ACSVM_CodeList_BinaryOpSet(ShRI) +ACSVM_CodeList_BinaryOpSet(SubU) +#undef ACSVM_CodeList_BinaryOpSet +ACSVM_CodeList(CmpI_GE, 0) +ACSVM_CodeList(CmpI_GT, 0) +ACSVM_CodeList(CmpI_LE, 0) +ACSVM_CodeList(CmpI_LT, 0) +ACSVM_CodeList(CmpU_EQ, 0) +ACSVM_CodeList(CmpU_NE, 0) +ACSVM_CodeList(DivX, 0) +ACSVM_CodeList(LAnd, 0) +ACSVM_CodeList(LOrI, 0) +ACSVM_CodeList(MulX, 0) + +// Call codes. +ACSVM_CodeList(Call_Lit, 1) +ACSVM_CodeList(Call_Stk, 0) +ACSVM_CodeList(CallFunc, 2) +ACSVM_CodeList(CallFunc_Lit, 0) +ACSVM_CodeList(CallSpec, 2) +ACSVM_CodeList(CallSpec_Lit, 0) +ACSVM_CodeList(CallSpec_R1, 2) +ACSVM_CodeList(Retn, 0) + +// Drop codes. +ACSVM_CodeList(Drop_GblArr, 1) +ACSVM_CodeList(Drop_GblReg, 1) +ACSVM_CodeList(Drop_HubArr, 1) +ACSVM_CodeList(Drop_HubReg, 1) +ACSVM_CodeList(Drop_LocArr, 1) +ACSVM_CodeList(Drop_LocReg, 1) +ACSVM_CodeList(Drop_ModArr, 1) +ACSVM_CodeList(Drop_ModReg, 1) +ACSVM_CodeList(Drop_Nul, 0) +ACSVM_CodeList(Drop_ScrRet, 0) + +// Jump codes. +ACSVM_CodeList(Jcnd_Lit, 2) +ACSVM_CodeList(Jcnd_Nil, 1) +ACSVM_CodeList(Jcnd_Tab, 1) +ACSVM_CodeList(Jcnd_Tru, 1) +ACSVM_CodeList(Jump_Lit, 1) +ACSVM_CodeList(Jump_Stk, 0) + +// Push codes. +ACSVM_CodeList(Pfun_Lit, 1) +ACSVM_CodeList(Pstr_Stk, 0) +ACSVM_CodeList(Push_GblArr, 1) +ACSVM_CodeList(Push_GblReg, 1) +ACSVM_CodeList(Push_HubArr, 1) +ACSVM_CodeList(Push_HubReg, 1) +ACSVM_CodeList(Push_Lit, 1) +ACSVM_CodeList(Push_LitArr, 0) +ACSVM_CodeList(Push_LocArr, 1) +ACSVM_CodeList(Push_LocReg, 1) +ACSVM_CodeList(Push_ModArr, 1) +ACSVM_CodeList(Push_ModReg, 1) +ACSVM_CodeList(Push_StrArs, 0) + +// Script control codes. +ACSVM_CodeList(ScrDelay, 0) +ACSVM_CodeList(ScrDelay_Lit, 1) +ACSVM_CodeList(ScrHalt, 0) +ACSVM_CodeList(ScrRestart, 0) +ACSVM_CodeList(ScrTerm, 0) +ACSVM_CodeList(ScrWaitI, 0) +ACSVM_CodeList(ScrWaitI_Lit, 1) +ACSVM_CodeList(ScrWaitS, 0) +ACSVM_CodeList(ScrWaitS_Lit, 1) + +// Stack control codes. +ACSVM_CodeList(Copy, 0) +ACSVM_CodeList(Swap, 0) + +// Unary operator codes. +#define ACSVM_CodeList_UnaryOpSet(name) \ + ACSVM_CodeList(name##_GblArr, 1) \ + ACSVM_CodeList(name##_GblReg, 1) \ + ACSVM_CodeList(name##_HubArr, 1) \ + ACSVM_CodeList(name##_HubReg, 1) \ + ACSVM_CodeList(name##_LocArr, 1) \ + ACSVM_CodeList(name##_LocReg, 1) \ + ACSVM_CodeList(name##_ModArr, 1) \ + ACSVM_CodeList(name##_ModReg, 1) +ACSVM_CodeList_UnaryOpSet(DecU) +ACSVM_CodeList_UnaryOpSet(IncU) +#undef ACSVM_CodeList_UnaryOpSet +ACSVM_CodeList(InvU, 0) +ACSVM_CodeList(NegI, 0) +ACSVM_CodeList(NotU, 0) + +#undef ACSVM_CodeList +#endif + + +#ifdef ACSVM_CodeListACS0 + +ACSVM_CodeListACS0(Nop, 0, "", Nop, 0, None) +ACSVM_CodeListACS0(ScrTerm, 1, "", ScrTerm, 0, None) +ACSVM_CodeListACS0(ScrHalt, 2, "", ScrHalt, 0, None) +ACSVM_CodeListACS0(Push_Lit, 3, "W", Push_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_1, 4, "b", CallSpec, 1, None) +ACSVM_CodeListACS0(CallSpec_2, 5, "b", CallSpec, 2, None) +ACSVM_CodeListACS0(CallSpec_3, 6, "b", CallSpec, 3, None) +ACSVM_CodeListACS0(CallSpec_4, 7, "b", CallSpec, 4, None) +ACSVM_CodeListACS0(CallSpec_5, 8, "b", CallSpec, 5, None) +ACSVM_CodeListACS0(CallSpec_1L, 9, "bW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_2L, 10, "bWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_3L, 11, "bWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_4L, 12, "bWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_5L, 13, "bWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(AddU, 14, "", AddU, 2, None) +ACSVM_CodeListACS0(SubU, 15, "", SubU, 2, None) +ACSVM_CodeListACS0(MulU, 16, "", MulU, 2, None) +ACSVM_CodeListACS0(DivI, 17, "", DivI, 2, None) +ACSVM_CodeListACS0(ModI, 18, "", ModI, 2, None) +ACSVM_CodeListACS0(CmpU_EQ, 19, "", CmpU_EQ, 2, None) +ACSVM_CodeListACS0(CmpU_NE, 20, "", CmpU_NE, 2, None) +ACSVM_CodeListACS0(CmpI_LT, 21, "", CmpI_LT, 2, None) +ACSVM_CodeListACS0(CmpI_GT, 22, "", CmpI_GT, 2, None) +ACSVM_CodeListACS0(CmpI_LE, 23, "", CmpI_LE, 2, None) +ACSVM_CodeListACS0(CmpI_GE, 24, "", CmpI_GE, 2, None) +ACSVM_CodeListACS0(Drop_LocReg, 25, "bL", Drop_LocReg, 1, None) +ACSVM_CodeListACS0(Drop_ModReg, 26, "bO", Drop_ModReg, 1, None) +ACSVM_CodeListACS0(Drop_HubReg, 27, "bU", Drop_HubReg, 1, None) +ACSVM_CodeListACS0(Push_LocReg, 28, "bL", Push_LocReg, 1, None) +ACSVM_CodeListACS0(Push_ModReg, 29, "bO", Push_ModReg, 1, None) +ACSVM_CodeListACS0(Push_HubReg, 30, "bU", Push_HubReg, 1, None) +ACSVM_CodeListACS0(AddU_LocReg, 31, "bL", AddU_LocReg, 1, None) +ACSVM_CodeListACS0(AddU_ModReg, 32, "bO", AddU_ModReg, 1, None) +ACSVM_CodeListACS0(AddU_HubReg, 33, "bU", AddU_HubReg, 1, None) +ACSVM_CodeListACS0(SubU_LocReg, 34, "bL", SubU_LocReg, 1, None) +ACSVM_CodeListACS0(SubU_ModReg, 35, "bO", SubU_ModReg, 1, None) +ACSVM_CodeListACS0(SubU_HubReg, 36, "bU", SubU_HubReg, 1, None) +ACSVM_CodeListACS0(MulU_LocReg, 37, "bL", MulU_LocReg, 1, None) +ACSVM_CodeListACS0(MulU_ModReg, 38, "bO", MulU_ModReg, 1, None) +ACSVM_CodeListACS0(MulU_HubReg, 39, "bU", MulU_HubReg, 1, None) +ACSVM_CodeListACS0(DivI_LocReg, 40, "bL", DivI_LocReg, 1, None) +ACSVM_CodeListACS0(DivI_ModReg, 41, "bO", DivI_ModReg, 1, None) +ACSVM_CodeListACS0(DivI_HubReg, 42, "bU", DivI_HubReg, 1, None) +ACSVM_CodeListACS0(ModI_LocReg, 43, "bL", ModI_LocReg, 1, None) +ACSVM_CodeListACS0(ModI_ModReg, 44, "bO", ModI_ModReg, 1, None) +ACSVM_CodeListACS0(ModI_HubReg, 45, "bU", ModI_HubReg, 1, None) +ACSVM_CodeListACS0(IncU_LocReg, 46, "bL", IncU_LocReg, 1, None) +ACSVM_CodeListACS0(IncU_ModReg, 47, "bO", IncU_ModReg, 1, None) +ACSVM_CodeListACS0(IncU_HubReg, 48, "bU", IncU_HubReg, 1, None) +ACSVM_CodeListACS0(DecU_LocReg, 49, "bL", DecU_LocReg, 1, None) +ACSVM_CodeListACS0(DecU_ModReg, 50, "bO", DecU_ModReg, 1, None) +ACSVM_CodeListACS0(DecU_HubReg, 51, "bU", DecU_HubReg, 1, None) +ACSVM_CodeListACS0(Jump_Lit, 52, "WJ", Jump_Lit, 0, None) +ACSVM_CodeListACS0(Jcnd_Tru, 53, "WJ", Jcnd_Tru, 1, None) +ACSVM_CodeListACS0(Drop_Nul, 54, "", Drop_Nul, 1, None) +ACSVM_CodeListACS0(ScrDelay, 55, "", ScrDelay, 1, None) +ACSVM_CodeListACS0(ScrDelay_Lit, 56, "W", ScrDelay_Lit, 0, None) + +ACSVM_CodeListACS0(ScrRestart, 69, "", ScrRestart, 0, None) +ACSVM_CodeListACS0(LAnd, 70, "", LAnd, 2, None) +ACSVM_CodeListACS0(LOrI, 71, "", LOrI, 2, None) +ACSVM_CodeListACS0(AndU, 72, "", AndU, 2, None) +ACSVM_CodeListACS0(OrIU, 73, "", OrIU, 2, None) +ACSVM_CodeListACS0(OrXU, 74, "", OrXU, 2, None) +ACSVM_CodeListACS0(NotU, 75, "", NotU, 1, None) +ACSVM_CodeListACS0(ShLU, 76, "", ShLU, 2, None) +ACSVM_CodeListACS0(ShRI, 77, "", ShRI, 2, None) +ACSVM_CodeListACS0(NegI, 78, "", NegI, 1, None) +ACSVM_CodeListACS0(Jcnd_Nil, 79, "WJ", Jcnd_Nil, 1, None) + +ACSVM_CodeListACS0(ScrWaitI, 81, "", ScrWaitI, 1, None) +ACSVM_CodeListACS0(ScrWaitI_Lit, 82, "W", ScrWaitI_Lit, 0, None) + +ACSVM_CodeListACS0(Jcnd_Lit, 84, "WWJ", Jcnd_Lit, 1, None) +ACSVM_CodeListACS0(PrintPush, 85, "", CallFunc, 0, PrintPush) + +ACSVM_CodeListACS0(PrintString, 87, "", CallFunc, 1, PrintString) +ACSVM_CodeListACS0(PrintIntD, 88, "", CallFunc, 1, PrintIntD) +ACSVM_CodeListACS0(PrintChar, 89, "", CallFunc, 1, PrintChar) + +ACSVM_CodeListACS0(MulX, 136, "", MulX, 2, None) +ACSVM_CodeListACS0(DivX, 137, "", DivX, 2, None) + +ACSVM_CodeListACS0(PrintFixD, 157, "", CallFunc, 1, PrintFixD) + +ACSVM_CodeListACS0(Push_LitB, 167, "B", Push_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_1LB, 168, "BB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_2LB, 169, "BBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_3LB, 170, "BBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_4LB, 171, "BBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_5LB, 172, "BBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(ScrDelay_LB, 173, "B", ScrDelay_Lit, 0, None) +ACSVM_CodeListACS0(Push_LitArrB, 175, "", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit2B, 176, "BB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit3B, 177, "BBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit4B, 178, "BBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit5B, 179, "BBBBB", Push_LitArr, 0, None) + +ACSVM_CodeListACS0(Drop_GblReg, 181, "bG", Drop_GblReg, 1, None) +ACSVM_CodeListACS0(Push_GblReg, 182, "bG", Push_GblReg, 0, None) +ACSVM_CodeListACS0(AddU_GblReg, 183, "bG", AddU_GblReg, 1, None) +ACSVM_CodeListACS0(SubU_GblReg, 184, "bG", SubU_GblReg, 1, None) +ACSVM_CodeListACS0(MulU_GblReg, 185, "bG", MulU_GblReg, 1, None) +ACSVM_CodeListACS0(DivI_GblReg, 186, "bG", DivI_GblReg, 1, None) +ACSVM_CodeListACS0(ModI_GblReg, 187, "bG", ModI_GblReg, 1, None) +ACSVM_CodeListACS0(IncU_GblReg, 188, "bG", IncU_GblReg, 1, None) +ACSVM_CodeListACS0(DecU_GblReg, 189, "bG", DecU_GblReg, 1, None) + +ACSVM_CodeListACS0(Call_Lit, 203, "b", Call_Lit, 0, None) +ACSVM_CodeListACS0(Call_Nul, 204, "b", Call_Lit, 0, None) +ACSVM_CodeListACS0(Retn_Nul, 205, "", Retn, 0, None) +ACSVM_CodeListACS0(Retn_Stk, 206, "", Retn, 0, None) +ACSVM_CodeListACS0(Push_ModArr, 207, "bo", Push_ModArr, 1, None) +ACSVM_CodeListACS0(Drop_ModArr, 208, "bo", Drop_ModArr, 2, None) +ACSVM_CodeListACS0(AddU_ModArr, 209, "bo", AddU_ModArr, 2, None) +ACSVM_CodeListACS0(SubU_ModArr, 210, "bo", SubU_ModArr, 2, None) +ACSVM_CodeListACS0(MulU_ModArr, 211, "bo", MulU_ModArr, 2, None) +ACSVM_CodeListACS0(DivI_ModArr, 212, "bo", DivI_ModArr, 2, None) +ACSVM_CodeListACS0(ModI_ModArr, 213, "bo", ModI_ModArr, 2, None) +ACSVM_CodeListACS0(IncU_ModArr, 214, "bo", IncU_ModArr, 2, None) +ACSVM_CodeListACS0(DecU_ModArr, 215, "bo", DecU_ModArr, 2, None) +ACSVM_CodeListACS0(Copy, 216, "", Copy, 1, None) +ACSVM_CodeListACS0(Swap, 217, "", Swap, 2, None) + +ACSVM_CodeListACS0(Pstr_Stk, 225, "", Pstr_Stk, 1, None) +ACSVM_CodeListACS0(Push_HubArr, 226, "bu", Push_HubArr, 1, None) +ACSVM_CodeListACS0(Drop_HubArr, 227, "bu", Drop_HubArr, 2, None) +ACSVM_CodeListACS0(AddU_HubArr, 228, "bu", AddU_HubArr, 2, None) +ACSVM_CodeListACS0(SubU_HubArr, 229, "bu", SubU_HubArr, 2, None) +ACSVM_CodeListACS0(MulU_HubArr, 230, "bu", MulU_HubArr, 2, None) +ACSVM_CodeListACS0(DivI_HubArr, 231, "bu", DivI_HubArr, 2, None) +ACSVM_CodeListACS0(ModI_HubArr, 232, "bu", ModI_HubArr, 2, None) +ACSVM_CodeListACS0(IncU_HubArr, 233, "bu", IncU_HubArr, 2, None) +ACSVM_CodeListACS0(DecU_HubArr, 234, "bu", DecU_HubArr, 2, None) +ACSVM_CodeListACS0(Push_GblArr, 235, "bg", Push_GblArr, 1, None) +ACSVM_CodeListACS0(Drop_GblArr, 236, "bg", Drop_GblArr, 2, None) +ACSVM_CodeListACS0(AddU_GblArr, 237, "bg", AddU_GblArr, 2, None) +ACSVM_CodeListACS0(SubU_GblArr, 238, "bg", SubU_GblArr, 2, None) +ACSVM_CodeListACS0(MulU_GblArr, 239, "bg", MulU_GblArr, 2, None) +ACSVM_CodeListACS0(DivI_GblArr, 240, "bg", DivI_GblArr, 2, None) +ACSVM_CodeListACS0(ModI_GblArr, 241, "bg", ModI_GblArr, 2, None) +ACSVM_CodeListACS0(IncU_GblArr, 242, "bg", IncU_GblArr, 2, None) +ACSVM_CodeListACS0(DecU_GblArr, 243, "bg", DecU_GblArr, 2, None) + +ACSVM_CodeListACS0(StrLen, 253, "", CallFunc, 1, StrLen) + +ACSVM_CodeListACS0(Jcnd_Tab, 256, "", Jcnd_Tab, 1, None) +ACSVM_CodeListACS0(Drop_ScrRet, 257, "", Drop_ScrRet, 1, None) + +ACSVM_CodeListACS0(CallSpec_5R1, 263, "b", CallSpec_R1, 5, None) + +ACSVM_CodeListACS0(PrintModArr, 273, "", CallFunc, 2, PrintModArr) +ACSVM_CodeListACS0(PrintHubArr, 274, "", CallFunc, 2, PrintHubArr) +ACSVM_CodeListACS0(PrintGblArr, 275, "", CallFunc, 2, PrintGblArr) + +ACSVM_CodeListACS0(AndU_LocReg, 291, "bL", AndU_LocReg, 1, None) +ACSVM_CodeListACS0(AndU_ModReg, 292, "bO", AndU_ModReg, 1, None) +ACSVM_CodeListACS0(AndU_HubReg, 293, "bU", AndU_HubReg, 1, None) +ACSVM_CodeListACS0(AndU_GblReg, 294, "bG", AndU_GblReg, 1, None) +ACSVM_CodeListACS0(AndU_ModArr, 295, "bo", AndU_ModArr, 2, None) +ACSVM_CodeListACS0(AndU_HubArr, 296, "bu", AndU_HubArr, 2, None) +ACSVM_CodeListACS0(AndU_GblArr, 297, "bg", AndU_GblArr, 2, None) +ACSVM_CodeListACS0(OrXU_LocReg, 298, "bL", OrXU_LocReg, 1, None) +ACSVM_CodeListACS0(OrXU_ModReg, 299, "bO", OrXU_ModReg, 1, None) +ACSVM_CodeListACS0(OrXU_HubReg, 300, "bU", OrXU_HubReg, 1, None) +ACSVM_CodeListACS0(OrXU_GblReg, 301, "bG", OrXU_GblReg, 1, None) +ACSVM_CodeListACS0(OrXU_ModArr, 302, "bo", OrXU_ModArr, 2, None) +ACSVM_CodeListACS0(OrXU_HubArr, 303, "bu", OrXU_HubArr, 2, None) +ACSVM_CodeListACS0(OrXU_GblArr, 304, "bg", OrXU_GblArr, 2, None) +ACSVM_CodeListACS0(OrIU_LocReg, 305, "bL", OrIU_LocReg, 1, None) +ACSVM_CodeListACS0(OrIU_ModReg, 306, "bO", OrIU_ModReg, 1, None) +ACSVM_CodeListACS0(OrIU_HubReg, 307, "bU", OrIU_HubReg, 1, None) +ACSVM_CodeListACS0(OrIU_GblReg, 308, "bG", OrIU_GblReg, 1, None) +ACSVM_CodeListACS0(OrIU_ModArr, 309, "bo", OrIU_ModArr, 2, None) +ACSVM_CodeListACS0(OrIU_HubArr, 310, "bu", OrIU_HubArr, 2, None) +ACSVM_CodeListACS0(OrIU_GblArr, 311, "bg", OrIU_GblArr, 2, None) +ACSVM_CodeListACS0(ShLU_LocReg, 312, "bL", ShLU_LocReg, 1, None) +ACSVM_CodeListACS0(ShLU_ModReg, 313, "bO", ShLU_ModReg, 1, None) +ACSVM_CodeListACS0(ShLU_HubReg, 314, "bU", ShLU_HubReg, 1, None) +ACSVM_CodeListACS0(ShLU_GblReg, 315, "bG", ShLU_GblReg, 1, None) +ACSVM_CodeListACS0(ShLU_ModArr, 316, "bo", ShLU_ModArr, 2, None) +ACSVM_CodeListACS0(ShLU_HubArr, 317, "bu", ShLU_HubArr, 2, None) +ACSVM_CodeListACS0(ShLU_GblArr, 318, "bg", ShLU_GblArr, 2, None) +ACSVM_CodeListACS0(ShRI_LocReg, 319, "bL", ShRI_LocReg, 1, None) +ACSVM_CodeListACS0(ShRI_ModReg, 320, "bO", ShRI_ModReg, 1, None) +ACSVM_CodeListACS0(ShRI_HubReg, 321, "bU", ShRI_HubReg, 1, None) +ACSVM_CodeListACS0(ShRI_GblReg, 322, "bG", ShRI_GblReg, 1, None) +ACSVM_CodeListACS0(ShRI_ModArr, 323, "bo", ShRI_ModArr, 2, None) +ACSVM_CodeListACS0(ShRI_HubArr, 324, "bu", ShRI_HubArr, 2, None) +ACSVM_CodeListACS0(ShRI_GblArr, 325, "bg", ShRI_GblArr, 2, None) + +ACSVM_CodeListACS0(InvU, 330, "", InvU, 1, None) + +ACSVM_CodeListACS0(PrintIntB, 349, "", CallFunc, 1, PrintIntB) +ACSVM_CodeListACS0(PrintIntX, 350, "", CallFunc, 1, PrintIntX) +ACSVM_CodeListACS0(CallFunc, 351, "bh", CallFunc, 0, None) +ACSVM_CodeListACS0(PrintEndStr, 352, "", CallFunc, 0, PrintEndStr) +ACSVM_CodeListACS0(PrintModArrR, 353, "", CallFunc, 4, PrintModArr) +ACSVM_CodeListACS0(PrintHubArrR, 354, "", CallFunc, 4, PrintHubArr) +ACSVM_CodeListACS0(PrintGblArrR, 355, "", CallFunc, 4, PrintGblArr) +ACSVM_CodeListACS0(StrCpyModArr, 356, "", CallFunc, 6, StrCpyModArr) +ACSVM_CodeListACS0(StrCpyHubArr, 357, "", CallFunc, 6, StrCpyHubArr) +ACSVM_CodeListACS0(StrCpyGblArr, 358, "", CallFunc, 6, StrCpyGblArr) +ACSVM_CodeListACS0(Pfun_Lit, 359, "b", Pfun_Lit, 0, None) +ACSVM_CodeListACS0(Call_Stk, 360, "", Call_Stk, 1, None) +ACSVM_CodeListACS0(ScrWaitS, 361, "", ScrWaitS, 1, None) + +ACSVM_CodeListACS0(Jump_Stk, 363, "", Jump_Stk, 1, None) +ACSVM_CodeListACS0(Drop_LocArr, 364, "bl", Drop_LocArr, 2, None) +ACSVM_CodeListACS0(Push_LocArr, 365, "bl", Push_LocArr, 1, None) +ACSVM_CodeListACS0(AddU_LocArr, 366, "bl", AddU_LocArr, 2, None) +ACSVM_CodeListACS0(SubU_LocArr, 367, "bl", SubU_LocArr, 2, None) +ACSVM_CodeListACS0(MulU_LocArr, 368, "bl", MulU_LocArr, 2, None) +ACSVM_CodeListACS0(DivI_LocArr, 369, "bl", DivI_LocArr, 2, None) +ACSVM_CodeListACS0(ModI_LocArr, 370, "bl", ModI_LocArr, 2, None) +ACSVM_CodeListACS0(IncU_LocArr, 371, "bl", IncU_LocArr, 2, None) +ACSVM_CodeListACS0(DecU_LocArr, 372, "bl", DecU_LocArr, 2, None) +ACSVM_CodeListACS0(AndU_LocArr, 373, "bl", AndU_LocArr, 2, None) +ACSVM_CodeListACS0(OrXU_LocArr, 374, "bl", OrXU_LocArr, 2, None) +ACSVM_CodeListACS0(OrIU_LocArr, 375, "bl", OrIU_LocArr, 2, None) +ACSVM_CodeListACS0(ShLU_LocArr, 376, "bl", ShLU_LocArr, 2, None) +ACSVM_CodeListACS0(ShRI_LocArr, 377, "bl", ShRI_LocArr, 2, None) +ACSVM_CodeListACS0(PrintLocArr, 378, "", CallFunc, 2, PrintLocArr) +ACSVM_CodeListACS0(PrintLocArrR, 379, "", CallFunc, 4, PrintLocArr) +ACSVM_CodeListACS0(StrCpyLocArr, 380, "", CallFunc, 6, StrCpyLocArr) + +ACSVM_CodeListACS0(CallSpec_6, 500, "b", CallSpec, 6, None) +ACSVM_CodeListACS0(CallSpec_7, 501, "b", CallSpec, 7, None) +ACSVM_CodeListACS0(CallSpec_8, 502, "b", CallSpec, 8, None) +ACSVM_CodeListACS0(CallSpec_9, 503, "b", CallSpec, 9, None) +ACSVM_CodeListACS0(CallSpec_10, 504, "b", CallSpec, 10, None) +ACSVM_CodeListACS0(CallSpec_6L, 505, "bWWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_7L, 506, "bWWWWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_8L, 507, "bWWWWWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_9L, 508, "bWWWWWWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_10L, 509, "bWWWWWWWWWWW", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_6LB, 510, "BBBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_7LB, 511, "BBBBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_8LB, 512, "BBBBBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_9LB, 513, "BBBBBBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(CallSpec_10LB,514, "BBBBBBBBBBB", CallSpec_Lit, 0, None) +ACSVM_CodeListACS0(Push_Lit6B, 515, "BBBBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit7B, 516, "BBBBBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit8B, 517, "BBBBBBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit9B, 518, "BBBBBBBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(Push_Lit10B, 519, "BBBBBBBBBB", Push_LitArr, 0, None) +ACSVM_CodeListACS0(CallSpec_10R1,520, "b", CallSpec_R1, 10, None) + +#undef ACSVM_CodeListACS0 +#endif + + +#ifdef ACSVM_FuncList + +ACSVM_FuncList(Nop) +ACSVM_FuncList(Kill) + +// Printing functions. +ACSVM_FuncList(PrintChar) +ACSVM_FuncList(PrintEndStr) +ACSVM_FuncList(PrintFixD) +ACSVM_FuncList(PrintGblArr) +ACSVM_FuncList(PrintHubArr) +ACSVM_FuncList(PrintIntB) +ACSVM_FuncList(PrintIntD) +ACSVM_FuncList(PrintIntX) +ACSVM_FuncList(PrintLocArr) +ACSVM_FuncList(PrintModArr) +ACSVM_FuncList(PrintPush) +ACSVM_FuncList(PrintString) + +// Script functions. +ACSVM_FuncList(ScrPauseS) +ACSVM_FuncList(ScrStartS) +ACSVM_FuncList(ScrStartSD) // Locked Door +ACSVM_FuncList(ScrStartSF) // Forced +ACSVM_FuncList(ScrStartSL) // Locked +ACSVM_FuncList(ScrStartSR) // Result +ACSVM_FuncList(ScrStopS) + +// String functions. +ACSVM_FuncList(GetChar) +ACSVM_FuncList(StrCaseCmp) +ACSVM_FuncList(StrCmp) +ACSVM_FuncList(StrCpyGblArr) +ACSVM_FuncList(StrCpyHubArr) +ACSVM_FuncList(StrCpyLocArr) +ACSVM_FuncList(StrCpyModArr) +ACSVM_FuncList(StrLeft) +ACSVM_FuncList(StrLen) +ACSVM_FuncList(StrMid) +ACSVM_FuncList(StrRight) + +#undef ACSVM_FuncList +#endif + + +#ifdef ACSVM_FuncListACS0 + +ACSVM_FuncListACS0(GetChar, 15, GetChar, {{2, Code::Push_StrArs}}) + +ACSVM_FuncListACS0(ScrStartS, 39, ScrStartS, {}) +ACSVM_FuncListACS0(ScrPauseS, 40, ScrPauseS, {}) +ACSVM_FuncListACS0(ScrStopS, 41, ScrStopS, {}) +ACSVM_FuncListACS0(ScrStartSL, 42, ScrStartSL, {}) +ACSVM_FuncListACS0(ScrStartSD, 43, ScrStartSD, {}) +ACSVM_FuncListACS0(ScrStartSR, 44, ScrStartSR, {}) +ACSVM_FuncListACS0(ScrStartSF, 45, ScrStartSF, {}) + +ACSVM_FuncListACS0(StrCmp, 63, StrCmp, {}) +ACSVM_FuncListACS0(StrCaseCmp, 64, StrCaseCmp, {}) +ACSVM_FuncListACS0(StrLeft, 65, StrLeft, {}) +ACSVM_FuncListACS0(StrRight, 66, StrRight, {}) +ACSVM_FuncListACS0(StrMid, 67, StrMid, {}) + +#undef ACSVM_FuncListACS0 +#endif + +// EOF + diff --git a/src/acs/vm/ACSVM/Environment.cpp b/src/acs/vm/ACSVM/Environment.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6461ec5140059e3dab85f2f75af673e89634a2a2 --- /dev/null +++ b/src/acs/vm/ACSVM/Environment.cpp @@ -0,0 +1,908 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Environment class. +// +//----------------------------------------------------------------------------- + +#include "Environment.hpp" + +#include "Action.hpp" +#include "BinaryIO.hpp" +#include "CallFunc.hpp" +#include "Code.hpp" +#include "CodeData.hpp" +#include "Function.hpp" +#include "HashMap.hpp" +#include "Module.hpp" +#include "PrintBuf.hpp" +#include "Scope.hpp" +#include "Script.hpp" +#include "Serial.hpp" +#include "Thread.hpp" + +#include <iostream> +#include <list> +#include <unordered_map> +#include <vector> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Environment::PrivData + // + struct Environment::PrivData + { + using FuncName = std::pair<ModuleName, String *>; + using FuncElem = HashMapElem<FuncName, Word>; + + struct NameEqual + { + bool operator () (ModuleName const *l, ModuleName const *r) const + {return *l == *r;} + }; + + struct NameHash + { + std::size_t operator () (ModuleName const *name) const + {return name->hash();} + }; + + struct FuncNameHash + { + std::size_t operator () (FuncName const &name) const + {return name.first.hash() + name.second->hash;} + }; + + + // Reserve index 0 as no function. + std::vector<Function *> functionByIdx{nullptr}; + + HashMapKeyExt<FuncName, Word, FuncNameHash> functionByName{16, 16}; + + HashMapKeyMem<ModuleName, Module, &Module::name, &Module::hashLink> modules; + + HashMapKeyMem<Word, GlobalScope, &GlobalScope::id, &GlobalScope::hashLink> scopes; + + std::vector<CallFunc> tableCallFunc + { + #define ACSVM_FuncList(name) \ + CallFunc_Func_##name, + #include "CodeList.hpp" + + CallFunc_Func_Nop + }; + + std::unordered_map<Word, CodeDataACS0> tableCodeDataACS0 + { + #define ACSVM_CodeListACS0(name, code, args, transCode, stackArgC, transFunc) \ + {code, {CodeACS0::name, args, Code::transCode, stackArgC, Func::transFunc}}, + #include "CodeList.hpp" + }; + + std::unordered_map<Word, FuncDataACS0> tableFuncDataACS0 + { + #define ACSVM_FuncListACS0(name, func, transFunc, ...) \ + {func, {FuncACS0::name, Func::transFunc, __VA_ARGS__}}, + #include "CodeList.hpp" + }; + }; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Environment constructor + // + Environment::Environment() : + branchLimit {0}, + scriptLocRegC{ScriptLocRegCDefault}, + + funcV{nullptr}, + funcC{0}, + + pd{new PrivData} + { + funcV = pd->functionByIdx.data(); + funcC = pd->functionByIdx.size(); + } + + // + // Environment destructor + // + Environment::~Environment() + { + pd->functionByName.free(); + pd->modules.free(); + pd->scopes.free(); + + while(scriptAction.next->obj) + delete scriptAction.next->obj; + + delete pd; + + // Deallocate threads. Do this after scopes have been destructed. + while(threadFree.next->obj) + delete threadFree.next->obj; + } + + // + // Environment::addCallFunc + // + Word Environment::addCallFunc(CallFunc func) + { + pd->tableCallFunc.push_back(func); + return pd->tableCallFunc.size() - 1; + } + + // + // Environment::addCodeDataACS0 + // + void Environment::addCodeDataACS0(Word code, CodeDataACS0 &&data) + { + auto itr = pd->tableCodeDataACS0.find(code); + if(itr == pd->tableCodeDataACS0.end()) + pd->tableCodeDataACS0.emplace(code, std::move(data)); + else + itr->second = std::move(data); + } + + // + // Environment::addFuncDataACS0 + // + void Environment::addFuncDataACS0(Word func, FuncDataACS0 &&data) + { + auto itr = pd->tableFuncDataACS0.find(func); + if(itr == pd->tableFuncDataACS0.end()) + pd->tableFuncDataACS0.emplace(func, std::move(data)); + else + itr->second = std::move(data); + } + + // + // Environment::allocThread + // + Thread *Environment::allocThread() + { + return new Thread(this); + } + + // + // Environment::callFunc + // + bool Environment::callFunc(Thread *thread, Word func, Word const *argV, Word argC) + { + return pd->tableCallFunc[func](thread, argV, argC); + } + + // + // Environment::callSpec + // + Word Environment::callSpec(Thread *thread, Word spec, Word const *argV, Word argC) + { + if(thread->scopeMap->clampCallSpec && thread->module->isACS0) + { + Vector<Word> argTmp{argV, argC}; + for(auto &arg : argTmp) arg &= 0xFF; + return callSpecImpl(thread, spec, argTmp.data(), argTmp.size()); + } + else + return callSpecImpl(thread, spec, argV, argC); + } + + // + // Environment::callSpecImpl + // + Word Environment::callSpecImpl(Thread *, Word, Word const *, Word) + { + return 0; + } + + // + // Environment::checkLock + // + bool Environment::checkLock(Thread *, Word, bool) + { + return false; + } + + // + // Environment::checkTag + // + bool Environment::checkTag(Word, Word) + { + return false; + } + + // + // Environment::collectStrings + // + void Environment::collectStrings() + { + stringTable.collectBegin(); + refStrings(); + stringTable.collectEnd(); + } + + // + // Environment::countActiveThread + // + std::size_t Environment::countActiveThread() const + { + std::size_t n = 0; + + for(auto &scope : pd->scopes) + { + if(scope.active) + n += scope.countActiveThread(); + } + + return n; + } + + // + // Environment::deferAction + // + void Environment::deferAction(ScriptAction &&action) + { + (new ScriptAction(std::move(action)))->link.insert(&scriptAction); + } + + // + // Environment::exec + // + void Environment::exec() + { + // Delegate deferred script actions. + for(auto itr = scriptAction.begin(), end = scriptAction.end(); itr != end;) + { + auto scope = pd->scopes.find(itr->id.global); + if(scope && scope->active) + itr++->link.relink(&scope->scriptAction); + else + ++itr; + } + + for(auto &scope : pd->scopes) + { + if(scope.active) + scope.exec(); + } + } + + // + // Environment::findCodeDataACS0 + // + CodeDataACS0 const *Environment::findCodeDataACS0(Word code) + { + auto itr = pd->tableCodeDataACS0.find(code); + return itr == pd->tableCodeDataACS0.end() ? nullptr : &itr->second; + } + + // + // Environment::findFuncDataACS0 + // + FuncDataACS0 const *Environment::findFuncDataACS0(Word func) + { + auto itr = pd->tableFuncDataACS0.find(func); + return itr == pd->tableFuncDataACS0.end() ? nullptr : &itr->second; + } + + // + // Environment::findModule + // + Module *Environment::findModule(ModuleName const &name) const + { + return pd->modules.find(name); + } + + // + // Environment::freeFunction + // + void Environment::freeFunction(Function *func) + { + // Null every reference to this function in every Module. + // O(N*M) is not very nice, but that can be fixed if/when it comes up. + for(auto &module : pd->modules) + { + for(Function *&funcItr : module.functionV) + { + if(funcItr == func) + funcItr = nullptr; + } + } + + pd->functionByIdx[func->idx] = nullptr; + delete func; + } + + // + // Environment::freeGlobalScope + // + void Environment::freeGlobalScope(GlobalScope *scope) + { + pd->scopes.unlink(scope); + delete scope; + } + + // + // Environment::freeModule + // + void Environment::freeModule(Module *module) + { + pd->modules.unlink(module); + delete module; + } + + // + // Environment::freeThread + // + void Environment::freeThread(Thread *thread) + { + thread->link.relink(&threadFree); + } + + // + // Environment::getCodeData + // + CodeData const *Environment::getCodeData(Code code) + { + switch(code) + { + #define ACSVM_CodeList(name, argc) case Code::name: \ + {static CodeData const data{Code::name, argc}; return &data;} + #include "CodeList.hpp" + + default: + case Code::None: + static CodeData const dataNone{Code::None, 0}; + return &dataNone; + } + } + + // + // Environment::getFreeThread + // + Thread *Environment::getFreeThread() + { + if(threadFree.next->obj) + { + Thread *thread = threadFree.next->obj; + thread->link.unlink(); + return thread; + } + else + return allocThread(); + } + + // + // Environment::getFunction + // + Function *Environment::getFunction(Module *module, String *funcName) + { + if(funcName) + { + PrivData::FuncName namePair{module->name, funcName}; + auto idx = pd->functionByName.find(namePair); + + if(!idx) + { + #if SIZE_MAX > UINT32_MAX + if(pd->functionByIdx.size() > UINT32_MAX) + throw std::bad_alloc(); + #endif + + idx = new PrivData::FuncElem{std::move(namePair), + static_cast<Word>(pd->functionByIdx.size())}; + pd->functionByName.insert(idx); + + pd->functionByIdx.emplace_back(); + funcV = pd->functionByIdx.data(); + funcC = pd->functionByIdx.size(); + } + + auto &ptr = pd->functionByIdx[idx->val]; + + if(!ptr) + ptr = new Function{module, funcName, idx->val}; + + return ptr; + } + else + return new Function{module, nullptr, 0}; + } + + // + // Environment::getGlobalScope + // + GlobalScope *Environment::getGlobalScope(Word id) + { + if(auto *scope = pd->scopes.find(id)) + return scope; + + auto scope = new GlobalScope(this, id); + pd->scopes.insert(scope); + return scope; + } + + // + // Environment::getModule + // + Module *Environment::getModule(ModuleName const &name) + { + auto module = pd->modules.find(name); + + if(!module) + { + module = new Module{this, name}; + pd->modules.insert(module); + loadModule(module); + } + else + { + if(!module->loaded) + loadModule(module); + } + + return module; + } + + // + // Environment::getModuleName + // + ModuleName Environment::getModuleName(char const *str) + { + return getModuleName(str, std::strlen(str)); + } + + // + // Environment::getModuleName + // + ModuleName Environment::getModuleName(char const *str, std::size_t len) + { + return {getString(str, len), nullptr, 0}; + } + + // + // Environment::hasActiveThread + // + bool Environment::hasActiveThread() const + { + for(auto &scope : pd->scopes) + { + if(scope.active && scope.hasActiveThread()) + return true; + } + + return false; + } + + // + // Environment::loadFunctions + // + void Environment::loadFunctions(Serial &in) + { + // Function index map. + pd->functionByName.free(); + for(std::size_t n = ReadVLN<std::size_t>(in); n--;) + { + ModuleName name = readModuleName(in); + String *str = &stringTable[ReadVLN<Word>(in)]; + Word idx = ReadVLN<Word>(in); + + pd->functionByName.insert(new PrivData::FuncElem{{name, str}, idx}); + } + + // Function vector. + auto oldTable = pd->functionByIdx; + + pd->functionByIdx.clear(); + pd->functionByIdx.resize(ReadVLN<std::size_t>(in), nullptr); + funcV = pd->functionByIdx.data(); + funcC = pd->functionByIdx.size(); + + // Reset function indexes. + for(Function *&func : oldTable) + { + if(func) + { + auto idx = pd->functionByName.find({func->module->name, func->name}); + func->idx = idx ? idx->val : 0; + pd->functionByIdx[func->idx] = func; + } + } + } + + // + // Environment::loadGlobalScopes + // + void Environment::loadGlobalScopes(Serial &in) + { + // Clear existing scopes. + pd->scopes.free(); + + for(auto n = ReadVLN<std::size_t>(in); n--;) + getGlobalScope(ReadVLN<Word>(in))->loadState(in); + } + + // + // Environment::loadScriptActions + // + void Environment::loadScriptActions(Serial &in) + { + readScriptActions(in, scriptAction); + } + + // + // Environment::loadState + // + void Environment::loadState(Serial &in) + { + in.readSign(Signature::Environment); + + loadStringTable(in); + loadFunctions(in); + loadGlobalScopes(in); + loadScriptActions(in); + + in.readSign(~Signature::Environment); + } + + // + // Environment::loadStringTable + // + void Environment::loadStringTable(Serial &in) + { + StringTable oldTable{std::move(stringTable)}; + stringTable.loadState(in); + resetStrings(); + } + + // + // Environment::printArray + // + void Environment::printArray(PrintBuf &buf, Array const &array, Word index, Word limit) + { + PrintArrayChar(buf, array, index, limit); + } + + // + // Environment::printKill + // + void Environment::printKill(Thread *thread, Word type, Word data) + { + std::cerr << "ACSVM ERROR: Kill " << type << ':' << data + << " at " << (thread->codePtr - thread->module->codeV.data() - 1) << '\n'; + } + + // + // Environment::readModuleName + // + ModuleName Environment::readModuleName(Serial &in) const + { + auto s = readString(in); + auto i = ReadVLN<std::size_t>(in); + + return {s, nullptr, i}; + } + + // + // Environment::readScript + // + Script *Environment::readScript(Serial &in) const + { + auto idx = ReadVLN<std::size_t>(in); + return &findModule(readModuleName(in))->scriptV[idx]; + } + + // + // Environment::readScriptAction + // + ScriptAction *Environment::readScriptAction(Serial &in) const + { + auto action = static_cast<ScriptAction::Action>(ReadVLN<int>(in)); + + Vector<Word> argV; + argV.alloc(ReadVLN<std::size_t>(in)); + for(auto &arg : argV) + arg = ReadVLN<Word>(in); + + ScopeID id; + id.global = ReadVLN<Word>(in); + id.hub = ReadVLN<Word>(in); + id.map = ReadVLN<Word>(in); + + ScriptName name = readScriptName(in); + + return new ScriptAction{id, name, action, std::move(argV)}; + } + + // + // Environment::readScriptActions + // + void Environment::readScriptActions(Serial &in, ListLink<ScriptAction> &out) const + { + // Clear existing actions. + while(out.next->obj) + delete out.next->obj; + + for(auto n = ReadVLN<std::size_t>(in); n--;) + readScriptAction(in)->link.insert(&out); + } + + // + // Environment::readScriptName + // + ScriptName Environment::readScriptName(Serial &in) const + { + String *s = in.in->get() ? &stringTable[ReadVLN<Word>(in)] : nullptr; + Word i = ReadVLN<Word>(in); + return {s, i}; + } + + // + // Environment::readString + // + String *Environment::readString(Serial &in) const + { + if(auto idx = ReadVLN<std::size_t>(in)) + return &stringTable[idx - 1]; + else + return nullptr; + } + + // + // Environment::refStrings + // + void Environment::refStrings() + { + for(auto &action : scriptAction) + action.refStrings(this); + + for(auto &funcIdx : pd->functionByName) + { + funcIdx.key.first.s->ref = true; + funcIdx.key.second->ref = true; + } + + for(auto &module : pd->modules) + module.refStrings(); + + for(auto &scope : pd->scopes) + scope.refStrings(); + } + + // + // Environment::resetStrings + // + void Environment::resetStrings() + { + for(auto &funcIdx : pd->functionByName) + { + funcIdx.key.first.s = getString(funcIdx.key.first.s); + funcIdx.key.second = getString(funcIdx.key.second); + } + + for(auto &module : pd->modules) + module.resetStrings(); + } + + // + // Environment::saveFunctions + // + void Environment::saveFunctions(Serial &out) const + { + WriteVLN(out, pd->functionByName.size()); + for(auto &funcIdx : pd->functionByName) + { + writeModuleName(out, funcIdx.key.first); + WriteVLN(out, funcIdx.key.second->idx); + WriteVLN(out, funcIdx.val); + } + + WriteVLN(out, pd->functionByIdx.size()); + } + + // + // Environment::saveGlobalScopes + // + void Environment::saveGlobalScopes(Serial &out) const + { + WriteVLN(out, pd->scopes.size()); + for(auto &scope : pd->scopes) + { + WriteVLN(out, scope.id); + scope.saveState(out); + } + } + + // + // Environment::saveScriptActions + // + void Environment::saveScriptActions(Serial &out) const + { + writeScriptActions(out, scriptAction); + } + + // + // Environment::saveState + // + void Environment::saveState(Serial &out) const + { + out.writeSign(Signature::Environment); + + saveStringTable(out); + saveFunctions(out); + saveGlobalScopes(out); + saveScriptActions(out); + + out.writeSign(~Signature::Environment); + } + + // + // Environment::saveStringTable + // + void Environment::saveStringTable(Serial &out) const + { + stringTable.saveState(out); + } + + // + // Environment::writeModuleName + // + void Environment::writeModuleName(Serial &out, ModuleName const &in) const + { + writeString(out, in.s); + WriteVLN(out, in.i); + } + + // + // Environment::writeScript + // + void Environment::writeScript(Serial &out, Script *in) const + { + WriteVLN(out, in - in->module->scriptV.data()); + writeModuleName(out, in->module->name); + } + + // + // Environment::writeScriptAction + // + void Environment::writeScriptAction(Serial &out, ScriptAction const *in) const + { + WriteVLN<int>(out, in->action); + + WriteVLN(out, in->argV.size()); + for(auto &arg : in->argV) + WriteVLN(out, arg); + + WriteVLN(out, in->id.global); + WriteVLN(out, in->id.hub); + WriteVLN(out, in->id.map); + + writeScriptName(out, in->name); + } + + // + // Environment::writeScriptActions + // + void Environment::writeScriptActions(Serial &out, + ListLink<ScriptAction> const &in) const + { + WriteVLN(out, in.size()); + + for(auto &action : in) + writeScriptAction(out, &action); + } + + // + // Environment::writeScriptName + // + void Environment::writeScriptName(Serial &out, ScriptName const &in) const + { + if(in.s) + { + out.out->put('\1'); + WriteVLN(out, in.s->idx); + } + else + out.out->put('\0'); + + WriteVLN(out, in.i); + } + + // + // Environment::writeString + // + void Environment::writeString(Serial &out, String const *in) const + { + if(in) + WriteVLN<std::size_t>(out, in->idx + 1); + else + WriteVLN<std::size_t>(out, 0); + } + + // + // Environment::PrintArrayChar + // + void Environment::PrintArrayChar(PrintBuf &buf, Array const &array, Word index, Word limit) + { + // Calculate output length and end index. + std::size_t len = 0; + Word end; + for(Word &itr = end = index; itr - index != limit; ++itr) + { + Word c = array.find(itr); + if(!c) break; + ++len; + } + + // Acquire output buffer. + buf.reserve(len); + char *s = buf.getBuf(len); + + // Truncate elements to char. + for(Word itr = index; itr != end; ++itr) + *s++ = array.find(itr); + } + + // + // Environment::PrintArrayUTF8 + // + void Environment::PrintArrayUTF8(PrintBuf &buf, Array const &array, Word index, Word limit) + { + // Calculate output length and end index. + std::size_t len = 0; + Word end; + for(Word &itr = end = index; itr - index != limit; ++itr) + { + Word c = array.find(itr); + if(!c) break; + if(c > 0x10FFFF) c = 0xFFFD; + + if(c <= 0x007F) len += 1; + else if(c <= 0x07FF) len += 2; + else if(c <= 0xFFFF) len += 3; + else len += 4; + } + + // Acquire output buffer. + buf.reserve(len); + char *s = buf.getBuf(len); + + // Convert UTF-32 sequence to UTF-8. + for(Word itr = index; itr != end; ++itr) + { + Word c = array.find(itr); + if(c > 0x10FFFF) c = 0xFFFD; + + if(c <= 0x7F) {*s++ = 0x00 | (c >> 0); goto put0;} + if(c <= 0x7FF) {*s++ = 0xC0 | (c >> 6); goto put1;} + if(c <= 0xFFFF) {*s++ = 0xE0 | (c >> 12); goto put2;} + {*s++ = 0xF0 | (c >> 18); goto put3;} + + put3: *s++ = 0x80 | ((c >> 12) & 0x3F); + put2: *s++ = 0x80 | ((c >> 6) & 0x3F); + put1: *s++ = 0x80 | ((c >> 0) & 0x3F); + put0:; + } + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Environment.hpp b/src/acs/vm/ACSVM/Environment.hpp new file mode 100644 index 0000000000000000000000000000000000000000..975a5af88375b9880e29ac9b1a6b1fb5f6c7a296 --- /dev/null +++ b/src/acs/vm/ACSVM/Environment.hpp @@ -0,0 +1,203 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Environment class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Environment_H__ +#define ACSVM__Environment_H__ + +#include "List.hpp" +#include "String.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Environment + // + // Represents an entire ACS environment. + // + class Environment + { + public: + Environment(); + virtual ~Environment(); + + Word addCallFunc(CallFunc func); + + void addCodeDataACS0(Word code, CodeDataACS0 &&data); + void addFuncDataACS0(Word func, FuncDataACS0 &&data); + + virtual bool callFunc(Thread *thread, Word func, Word const *argV, Word argC); + Word callSpec(Thread *thread, Word spec, Word const *argV, Word argC); + + // Function to check if a lock can be opened. Default behavior is to + // always return false. + virtual bool checkLock(Thread *thread, Word lock, bool door); + + // Function to check tags. Must return true to indicate script should + // continue. Default behavior is to always return false. + virtual bool checkTag(Word type, Word tag); + + void collectStrings(); + + std::size_t countActiveThread() const; + + void deferAction(ScriptAction &&action); + + virtual void exec(); + + CodeDataACS0 const *findCodeDataACS0(Word code); + FuncDataACS0 const *findFuncDataACS0(Word func); + + Module *findModule(ModuleName const &name) const; + + // Used by Module when unloading. + void freeFunction(Function *func); + + void freeGlobalScope(GlobalScope *scope); + + void freeModule(Module *module); + + void freeThread(Thread *thread); + + CodeData const *getCodeData(Code code); + + Thread *getFreeThread(); + + Function *getFunction(Word idx) {return idx < funcC ? funcV[idx] : nullptr;} + + Function *getFunction(Module *module, String *name); + + GlobalScope *getGlobalScope(Word id); + + // Gets the named module, loading it if needed. + Module *getModule(ModuleName const &name); + + ModuleName getModuleName(char const *str); + virtual ModuleName getModuleName(char const *str, std::size_t len); + + // Called to translate script type from ACS0 script number. + // Default behavior is to modulus 1000 the name. + virtual std::pair<Word /*type*/, Word /*name*/> getScriptTypeACS0(Word name) + {return {name / 1000, name % 1000};} + + // Called to translate script type from ACSE SPTR. + // Default behavior is to return the type as-is. + virtual Word getScriptTypeACSE(Word type) {return type;} + + String *getString(Word idx) {return &stringTable[~idx];} + + String *getString(char const *first, char const *last) + {return &stringTable[{first, last}];} + + String *getString(char const *str) + {return getString(str, std::strlen(str));} + + String *getString(char const *str, std::size_t len) + {return &stringTable[{str, len}];} + + String *getString(StringData const *data) + {return data ? &stringTable[*data] : nullptr;} + + // Returns true if any contained scope is active and has an active thread. + bool hasActiveThread() const; + + virtual void loadState(Serial &in); + + // Prints an array to a print buffer. Default behavior is PrintArrayChar. + virtual void printArray(PrintBuf &buf, Array const &array, Word index, Word limit); + + // Function to print Kill instructions. Default behavior is to print + // message to stderr. + virtual void printKill(Thread *thread, Word type, Word data); + + // Deserializes a ModuleName. Default behavior is to load s and i. + virtual ModuleName readModuleName(Serial &in) const; + + Script *readScript(Serial &in) const; + ScriptAction *readScriptAction(Serial &in) const; + void readScriptActions(Serial &in, ListLink<ScriptAction> &out) const; + ScriptName readScriptName(Serial &in) const; + String *readString(Serial &in) const; + + virtual void refStrings(); + + virtual void resetStrings(); + + virtual void saveState(Serial &out) const; + + // Serializes a ModuleName. Default behavior is to save s and i. + virtual void writeModuleName(Serial &out, ModuleName const &name) const; + + void writeScript(Serial &out, Script *in) const; + void writeScriptAction(Serial &out, ScriptAction const *in) const; + void writeScriptActions(Serial &out, ListLink<ScriptAction> const &in) const; + void writeScriptName(Serial &out, ScriptName const &in) const; + void writeString(Serial &out, String const *in) const; + + StringTable stringTable; + + // Number of branches allowed per call to Thread::exec. Default of 0 + // means no limit. + Word branchLimit; + + // Default number of script variables. Default is 20. + Word scriptLocRegC; + + + // Prints an array to a print buffer, truncating elements of the array to + // fit char. + static void PrintArrayChar(PrintBuf &buf, Array const &array, Word index, Word limit); + + // Prints an array to a print buffer, converting the array as a UTF-32 + // sequence into a UTF-8 sequence. + static void PrintArrayUTF8(PrintBuf &buf, Array const &array, Word index, Word limit); + + static constexpr Word ScriptLocRegCDefault = 20; + + protected: + virtual Thread *allocThread(); + + // Called by callSpec after processing arguments. Default behavior is to + // do nothing and return 0. + virtual Word callSpecImpl(Thread *thread, Word spec, Word const *argV, Word argC); + + virtual void loadModule(Module *module) = 0; + + ListLink<ScriptAction> scriptAction; + ListLink<Thread> threadFree; + + Function **funcV; + std::size_t funcC; + + private: + struct PrivData; + + void loadFunctions(Serial &in); + void loadGlobalScopes(Serial &in); + void loadScriptActions(Serial &in); + void loadStringTable(Serial &in); + + void saveFunctions(Serial &out) const; + void saveGlobalScopes(Serial &out) const; + void saveScriptActions(Serial &out) const; + void saveStringTable(Serial &out) const; + + PrivData *pd; + }; +} + +#endif//ACSVM__Environment_H__ + diff --git a/src/acs/vm/ACSVM/Error.cpp b/src/acs/vm/ACSVM/Error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85de5cbbd14421282882db4417605322f757591d --- /dev/null +++ b/src/acs/vm/ACSVM/Error.cpp @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Error classes. +// +//----------------------------------------------------------------------------- + +#include "Error.hpp" + +#include "BinaryIO.hpp" + +#include <cctype> +#include <cstdio> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // SerialSignError constructor + // + SerialSignError::SerialSignError(Signature sig_, Signature got_) + { + auto sig = static_cast<std::uint32_t>(sig_); + auto got = static_cast<std::uint32_t>(got_); + + WriteLE4(reinterpret_cast<Byte *>(buf + SigS), sig); + WriteLE4(reinterpret_cast<Byte *>(buf + GotS), got); + for(auto i : {SigS+0, SigS+1, SigS+2, SigS+3, GotS+0, GotS+1, GotS+2, GotS+3}) + if(!std::isprint(buf[i]) && !std::isprint(buf[i] = ~buf[i])) buf[i] = ' '; + + for(int i = 8; i--;) buf[Sig + i] = "0123456789ABCDEF"[sig & 0xF], sig >>= 4; + for(int i = 8; i--;) buf[Got + i] = "0123456789ABCDEF"[got & 0xF], got >>= 4; + + msg = buf; + } + + // + // SerialSignError destructor + // + SerialSignError::~SerialSignError() + { + delete[] msg; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Error.hpp b/src/acs/vm/ACSVM/Error.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6d217a11158b531c005e5f0517486821e6ab642a --- /dev/null +++ b/src/acs/vm/ACSVM/Error.hpp @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Error classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Error_H__ +#define ACSVM__Error_H__ + +#include "Types.hpp" + +#include <stdexcept> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ReadError + // + // Generic exception class for errors occurring during bytecode reading. + // + class ReadError : public std::exception + { + public: + ReadError(char const *msg_ = "ACSVM::ReadError") : msg{msg_} {} + + virtual char const *what() const noexcept {return msg;} + + char const *const msg; + }; + + // + // SerialError + // + // Generic exception for errors during serialization. + // + class SerialError : public std::exception + { + public: + SerialError(char const *msg_) : msg{const_cast<char *>(msg_)} {} + + virtual char const *what() const noexcept {return msg;} + + protected: + SerialError() : msg{nullptr} {} + + char *msg; + }; + + // + // SerialSignError + // + // Thrown due to signature mismatch. + // + class SerialSignError : public SerialError + { + public: + SerialSignError(Signature sig, Signature got); + ~SerialSignError(); + + private: + static constexpr std::size_t Sig = 29, SigS = 39; + static constexpr std::size_t Got = 49, GotS = 59; + + char buf[sizeof("signature mismatch: expected XXXXXXXX (XXXX) got XXXXXXXX (XXXX)")] = + "signature mismatch: expected XXXXXXXX (XXXX) got XXXXXXXX (XXXX)"; + }; +} + +#endif//ACSVM__Error_H__ + diff --git a/src/acs/vm/ACSVM/Function.cpp b/src/acs/vm/ACSVM/Function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53e5e8f6c65d47475b706e5bb0c3ba0895213a35 --- /dev/null +++ b/src/acs/vm/ACSVM/Function.cpp @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Function class. +// +//----------------------------------------------------------------------------- + +#include "Function.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Function constructor + // + Function::Function(Module *module_, String *name_, Word idx_) : + module{module_}, + name {name_}, + idx {idx_}, + + argC {0}, + codeIdx{0}, + locArrC{0}, + locRegC{0}, + + flagRet{false} + { + } + + // + // Function destructor + // + Function::~Function() + { + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Function.hpp b/src/acs/vm/ACSVM/Function.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2186d537ba7aa6f26484beda728f03adf417d88e --- /dev/null +++ b/src/acs/vm/ACSVM/Function.hpp @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Function class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Function_H__ +#define ACSVM__Function_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Function + // + class Function + { + public: + Function(Module *module, String *name, Word idx); + ~Function(); + + Module *module; + String *name; + Word idx; + + Word argC; + Word codeIdx; + Word locArrC; + Word locRegC; + + bool flagRet : 1; + }; +} + +#endif//ACSVM__Function_H__ + diff --git a/src/acs/vm/ACSVM/HashMap.hpp b/src/acs/vm/ACSVM/HashMap.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b509e5119e10171b37c627be4442758aed461bf0 --- /dev/null +++ b/src/acs/vm/ACSVM/HashMap.hpp @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// HashMap class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__HashMap_H__ +#define ACSVM__HashMap_H__ + +#include "List.hpp" +#include "Types.hpp" +#include "Vector.hpp" + +#include <functional> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // HashMapGetKeyMem + // + // Used for HashMaps for which the Key is a member of T. + // + template<typename Key, typename T, Key const T::*KeyMem> + struct HashMapGetKeyMem + { + static Key const &Get(T *obj) {return obj->*KeyMem;} + }; + + // + // HashMapGetKeyObj + // + // Used for HashMaps for which the Key is a base class or the same as T. + // + template<typename Key, typename T> + struct HashMapGetKeyObj + { + static Key const &Get(T *obj) {return *obj;} + }; + + // + // HashMap + // + // Stores objects of type T that can be found by keys of type Key. + // + // GetKeyMem must be HashMapGetKeyMem or HashMapGetKeyObj. + // + // This class does not manage the lifetimes of the contained objects, and + // objects must be unlinked by the unlink function before being destructed. + // Although an exception is made to the latter if the clear function is + // called before any other. + // + template<typename Key, typename T, typename GetKey, ListLink<T> T::*LinkMem, + typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>> + class HashMap + { + private: + // + // Iterator + // + template<typename Obj> + class IteratorBase + { + public: + // + // operator ++ + // + IteratorBase<Obj> &operator ++ () + { + for(;;) + { + // Traverse to next link. + link = link->next; + + // If it has an object, we are done. + if(link->obj) break; + + // Otherwise, we are at the current chain's head. So increment + // to the next chain. If at the last chain, we are done. + if(++link == last) break; + } + + return *this; + } + + IteratorBase<Obj> operator ++ (int) {auto i = *this; ++*this; return i;} + + Obj &operator * () const {return *link->obj;} + Obj *operator -> () const {return link->obj;} + + bool operator == (IteratorBase<Obj> const &iter) const + {return iter.link == link;} + bool operator != (IteratorBase<Obj> const &iter) const + {return iter.link != link;} + + + friend class HashMap; + + private: + IteratorBase(ListLink<T> *link_, ListLink<T> *last_) : + link{link_}, last{last_} {if(link != last) ++*this;} + + ListLink<T> *link, *last; + }; + + public: + using const_iterator = IteratorBase<T const>; + using iterator = IteratorBase<T>; + using size_type = std::size_t; + + + HashMap() : chainV{16}, objC{0}, growC{16} {} + HashMap(size_type count, size_type growC_) : + chainV{count}, objC{0}, growC{growC_} {} + ~HashMap() {clear();} + + // begin + iterator begin() {return {chainV.begin(), chainV.end()};} + + // + // clear + // + void clear() + { + for(auto &chain : chainV) + { + while(auto obj = chain.next->obj) + (obj->*LinkMem).unlink(); + } + + objC = 0; + } + + // end + iterator end() {return {chainV.end(), chainV.end()};} + + // + // find + // + T *find(Key const &key) + { + for(auto itr = chainV[hasher(key) % chainV.size()].next; itr->obj; itr = itr->next) + { + if(equal(key, GetKey::Get(itr->obj))) + return itr->obj; + } + + return nullptr; + } + + // + // free + // + // Unlinks and deletes all contained objects. + // + void free() + { + for(auto &chain : chainV) + { + while(auto obj = chain.next->obj) + (obj->*LinkMem).unlink(), delete obj; + } + + objC = 0; + } + + // + // insert + // + void insert(T *obj) + { + if(objC >= chainV.size()) + resize(chainV.size() + chainV.size() / 2 + growC); + + ++objC; + (obj->*LinkMem).insert(&chainV[hasher(GetKey::Get(obj)) % chainV.size()]); + } + + // + // resize + // + // Reallocates to count chains. + // + void resize(size_type count) + { + auto oldChainV = std::move(chainV); + chainV.alloc(count); + + for(auto &chain : oldChainV) + { + while(auto obj = chain.next->obj) + (obj->*LinkMem).relink(&chainV[hasher(GetKey::Get(obj)) % chainV.size()]); + } + } + + // size + size_type size() const {return objC;} + + // + // unlink + // + void unlink(T *obj) + { + --objC; + (obj->*LinkMem).unlink(); + } + + private: + Vector<ListLink<T>> chainV; + Hash hasher; + KeyEqual equal; + + size_type objC; + size_type growC; + }; + + // + // HashMapKeyMem + // + // Convenience typedef for HashMapGetKeyMem-based HashMaps. + // + template<typename Key, typename T, Key const T::*KeyMem, + ListLink<T> T::*LinkMem, typename Hash = std::hash<Key>, + typename KeyEqual = std::equal_to<Key>> + using HashMapKeyMem = HashMap<Key, T, HashMapGetKeyMem<Key, T, KeyMem>, + LinkMem, Hash, KeyEqual>; + + // + // HashMapKeyObj + // + // Convenience typedef for HashMapGetKeyObj-based HashMaps. + // + template<typename Key, typename T, ListLink<T> T::*LinkMem, + typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>> + using HashMapKeyObj = HashMap<Key, T, HashMapGetKeyObj<Key, T>, LinkMem, + Hash, KeyEqual>; + + // + // HashMapElem + // + // Wraps a type with a key and link for use in HashMap. + // + template<typename Key, typename T> + class HashMapElem + { + public: + HashMapElem(Key const &key_, T const &val_) : + key{key_}, val{val_}, link{this} {} + + Key key; + T val; + + ListLink<HashMapElem<Key, T>> link; + }; + + // + // HashMapKeyExt + // + // Convenience typedef for HashMapElem-based HashMaps. + // + template<typename Key, typename T, typename Hash = std::hash<Key>, + typename KeyEqual = std::equal_to<Key>> + using HashMapKeyExt = HashMapKeyMem<Key, HashMapElem<Key, T>, + &HashMapElem<Key, T>::key, &HashMapElem<Key, T>::link, Hash, KeyEqual>; +} + +#endif//ACSVM__HashMap_H__ + diff --git a/src/acs/vm/ACSVM/HashMapFixed.hpp b/src/acs/vm/ACSVM/HashMapFixed.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0886d9b97dfa2cc477c1829c646fda12d0390df0 --- /dev/null +++ b/src/acs/vm/ACSVM/HashMapFixed.hpp @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// HashMapFixed class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__HashMapFixed_H__ +#define ACSVM__HashMapFixed_H__ + +#include "Types.hpp" + +#include <functional> +#include <new> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // HashMapFixed + // + // Non-resizable hash map. + // + template<typename Key, typename T, typename Hash = std::hash<Key>> + class HashMapFixed + { + public: + struct Elem + { + Key key; + T val; + Elem *next; + }; + + using iterator = Elem *; + using size_type = std::size_t; + using value_type = Elem; + + + HashMapFixed() : hasher{}, table{nullptr}, elemV{nullptr}, elemC{0} {} + ~HashMapFixed() {free();} + + // + // alloc + // + void alloc(size_type count) + { + if(elemV) free(); + + if(!count) return; + + size_type sizeRaw = sizeof(Elem) * count + sizeof(Elem *) * count; + + elemC = count; + elemV = static_cast<Elem *>(::operator new(sizeRaw)); + } + + // begin + iterator begin() {return elemV;} + + // + // build + // + void build() + { + // Initialize table. + table = reinterpret_cast<Elem **>(elemV + elemC); + for(Elem **elem = table + elemC; elem != table;) + *--elem = nullptr; + + // Insert elements. + for(Elem &elem : *this) + { + size_type hash = hasher(elem.key) % elemC; + + elem.next = table[hash]; + table[hash] = &elem; + } + } + + // empty + bool empty() const {return !elemC;} + + // end + iterator end() {return elemV + elemC;} + + // + // find + // + T *find(Key const &key) + { + if(!table) return nullptr; + + for(Elem *elem = table[hasher(key) % elemC]; elem; elem = elem->next) + { + if(elem->key == key) + return &elem->val; + } + + return nullptr; + } + + // + // free + // + void free() + { + if(table) + { + for(Elem *elem = elemV + elemC; elem != elemV;) + (--elem)->~Elem(); + + table = nullptr; + } + + ::operator delete(elemV); + + elemV = nullptr; + elemC = 0; + } + + // size + size_type size() const {return elemC;} + + private: + Hash hasher; + + Elem **table; + Elem *elemV; + size_type elemC; + }; +} + +#endif//ACSVM__HashMapFixed_H__ + diff --git a/src/acs/vm/ACSVM/ID.hpp b/src/acs/vm/ACSVM/ID.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fbb113cce0e457519b7be27ac3b749d43257e90d --- /dev/null +++ b/src/acs/vm/ACSVM/ID.hpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Numeric identifiers. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__ID_H__ +#define ACSVM__ID_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + constexpr std::uint32_t MakeID(char c0, char c1, char c2, char c3); + constexpr std::uint32_t MakeID(char const (&s)[5]); + + // + // MakeID + // + constexpr std::uint32_t MakeID(char c0, char c1, char c2, char c3) + { + return + (static_cast<std::uint32_t>(c0) << 0) | + (static_cast<std::uint32_t>(c1) << 8) | + (static_cast<std::uint32_t>(c2) << 16) | + (static_cast<std::uint32_t>(c3) << 24); + } + + // + // MakeID + // + constexpr std::uint32_t MakeID(char const (&s)[5]) + { + return MakeID(s[0], s[1], s[2], s[3]); + } +} + +#endif//ACSVM__ID_H__ + diff --git a/src/acs/vm/ACSVM/Init.cpp b/src/acs/vm/ACSVM/Init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66e65bc8113d0b0701e1567aa5d8026ea621599f --- /dev/null +++ b/src/acs/vm/ACSVM/Init.cpp @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Initializer handling. +// +//----------------------------------------------------------------------------- + +#include "Init.hpp" + +#include "Array.hpp" +#include "Function.hpp" +#include "Module.hpp" +#include "String.hpp" + +#include <vector> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ArrayInit::PrivData + // + struct ArrayInit::PrivData + { + std::vector<WordInit> initV; + }; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + + // + // ArrayInit constructor + // + ArrayInit::ArrayInit() : + pd{new PrivData} + { + } + + // + // ArrayInit destructor + // + ArrayInit::~ArrayInit() + { + delete pd; + } + + // + // ArrayInit::apply + // + void ArrayInit::apply(Array &arr, Module *module) + { + Word idx = 0; + for(WordInit &init : pd->initV) + { + Word value = init.getValue(module); + if(value) arr[idx] = value; + ++idx; + } + } + + // + // ArrayInit::finish + // + void ArrayInit::finish() + { + // Clear out trailing zeroes. + while(!pd->initV.empty() && !pd->initV.back()) + pd->initV.pop_back(); + + // Shrink vector. + pd->initV.shrink_to_fit(); + + // TODO: Break up initialization data into nonzero ranges. + } + + // + // ArrayInit::reserve + // + void ArrayInit::reserve(Word count) + { + pd->initV.resize(count, 0); + } + + // + // ArrayInit::setTag + // + void ArrayInit::setTag(Word idx, InitTag tag) + { + if(idx >= pd->initV.size()) + pd->initV.resize(idx + 1, 0); + + pd->initV[idx].tag = tag; + } + + // + // ArrayInit::setVal + // + void ArrayInit::setVal(Word idx, Word val) + { + if(idx >= pd->initV.size()) + pd->initV.resize(idx + 1, 0); + + pd->initV[idx].val = val; + } + + // + // WordInit::getValue + // + Word WordInit::getValue(Module *module) const + { + switch(tag) + { + case InitTag::Integer: + return val; + + case InitTag::Function: + if(val < module->functionV.size() && module->functionV[val]) + return module->functionV[val]->idx; + else + return val; + + case InitTag::String: + if(val < module->stringV.size()) + return ~module->stringV[val]->idx; + else + return val; + } + + return val; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Init.hpp b/src/acs/vm/ACSVM/Init.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aecbf700b9673b807173ac088d2ff24c60d2599d --- /dev/null +++ b/src/acs/vm/ACSVM/Init.hpp @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Initializer handling. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Init_H__ +#define ACSVM__Init_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // InitTag + // + enum class InitTag + { + Integer, + Function, + String, + }; + + // + // ArrayInit + // + class ArrayInit + { + public: + ArrayInit(); + ~ArrayInit(); + + void apply(Array &arr, Module *module); + + void finish(); + + void reserve(Word count); + + void setTag(Word idx, InitTag tag); + void setVal(Word idx, Word val); + + private: + struct PrivData; + + PrivData *pd; + }; + + // + // WordInit + // + class WordInit + { + public: + WordInit() = default; + WordInit(Word val_) : val{val_}, tag{InitTag::Integer} {} + WordInit(Word val_, InitTag tag_) : val{val_}, tag{tag_} {} + + explicit operator bool () const + {return val || tag != InitTag::Integer;} + + Word getValue(Module *module) const; + + Word val; + InitTag tag; + }; +} + +#endif//ACSVM__Init_H__ + diff --git a/src/acs/vm/ACSVM/Jump.cpp b/src/acs/vm/ACSVM/Jump.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa6efed3789239195369ed473fa416290a4e302c --- /dev/null +++ b/src/acs/vm/ACSVM/Jump.cpp @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Jump class. +// +//----------------------------------------------------------------------------- + +#include "Jump.hpp" + +#include "BinaryIO.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // JumpMap::loadJumps + // + void JumpMap::loadJumps(Byte const *data, std::size_t count) + { + table.alloc(count); + std::size_t iter = 0; + + for(auto &jump : table) + { + Word caseVal = ReadLE4(data + iter); iter += 4; + Word codeIdx = ReadLE4(data + iter); iter += 4; + new(&jump) HashMapFixed<Word, Word>::Elem{caseVal, codeIdx, nullptr}; + } + + table.build(); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Jump.hpp b/src/acs/vm/ACSVM/Jump.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1656de037aa2952cda4eeff771334854d7624502 --- /dev/null +++ b/src/acs/vm/ACSVM/Jump.hpp @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Jump class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Jump_H__ +#define ACSVM__Jump_H__ + +#include "HashMapFixed.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Jump + // + // Dynamic jump target. + // + class Jump + { + public: + Word codeIdx; + }; + + // + // JumpMap + // + class JumpMap + { + public: + void loadJumps(Byte const *data, std::size_t count); + + HashMapFixed<Word, Word> table; + }; +} + +#endif//ACSVM__Jump_H__ + diff --git a/src/acs/vm/ACSVM/List.hpp b/src/acs/vm/ACSVM/List.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3c65a2a350a9cb91b4b0f8bffac1038ebd0fb6c0 --- /dev/null +++ b/src/acs/vm/ACSVM/List.hpp @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Linked list handling. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__List_H__ +#define ACSVM__List_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ListLink + // + template<typename T> + class ListLink + { + private: + // + // IteratorBase + // + template<typename Obj> + class IteratorBase + { + public: + IteratorBase<Obj> &operator ++ () {link = link->next; return *this;} + IteratorBase<Obj> operator ++ (int) {auto i = *this; ++*this; return i;} + + Obj &operator * () const {return *link->obj;} + Obj *operator -> () const {return link->obj;} + + bool operator == (IteratorBase<Obj> const &iter) const + {return iter.link == link;} + bool operator != (IteratorBase<Obj> const &iter) const + {return iter.link != link;} + + + friend class ListLink; + + private: + IteratorBase(ListLink<T> const *link_) : link{link_} {} + + ListLink<T> const *link; + }; + + public: + ListLink() : obj{nullptr}, prev{this}, next{this} {} + ListLink(ListLink<T> const &) = delete; + ListLink(T *obj_) : obj{obj_}, prev{this}, next{this} {} + ListLink(T *obj_, ListLink<T> &&link) : + obj{obj_}, prev{link.prev}, next{link.next} + {prev->next = next->prev = this; link.prev = link.next = &link;} + ~ListLink() {unlink();} + + // begin + IteratorBase<T> begin() {return next;} + IteratorBase<T const> begin() const {return next;} + + // end + IteratorBase<T> end() {return this;} + IteratorBase<T const> end() const {return this;} + + // + // insert + // + void insert(ListLink<T> *head) + { + (prev = head->prev)->next = this; + (next = head )->prev = this; + } + + void relink(ListLink<T> *head) {unlink(); insert(head);} + + // + // size + // + std::size_t size() const + { + std::size_t count = 0; + for(auto const &o : *this) (void)o, ++count; + return count; + } + + // + // unlink + // + void unlink() + { + prev->next = next; + next->prev = prev; + + prev = next = this; + } + + T *const obj; + ListLink<T> *prev, *next; + }; +} + +#endif//ACSVM__List_H__ + diff --git a/src/acs/vm/ACSVM/Module.cpp b/src/acs/vm/ACSVM/Module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..95626be7b5ac878c268d346e2704e7d752cc34e7 --- /dev/null +++ b/src/acs/vm/ACSVM/Module.cpp @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Module class. +// +//----------------------------------------------------------------------------- + +#include "Module.hpp" + +#include "Array.hpp" +#include "Environment.hpp" +#include "Function.hpp" +#include "Init.hpp" +#include "Jump.hpp" +#include "Script.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // ModuleName::hash + // + std::size_t ModuleName::hash() const + { + return s->hash + std::hash<void*>()(p) + i; + } + + // + // Module constructor + // + Module::Module(Environment *env_, ModuleName const &name_) : + env{env_}, + name{name_}, + + hashLink{this}, + + isACS0{false}, + loaded{false} + { + } + + // + // Module destructor + // + Module::~Module() + { + reset(); + } + + // + // Module::refStrings + // + void Module::refStrings() const + { + if(name.s) name.s->ref = true; + + for(auto &s : arrImpV) if(s) s->ref = true; + for(auto &s : arrNameV) if(s) s->ref = true; + for(auto &s : funcNameV) if(s) s->ref = true; + for(auto &s : regImpV) if(s) s->ref = true; + for(auto &s : regNameV) if(s) s->ref = true; + for(auto &s : scrNameV) if(s) s->ref = true; + for(auto &s : stringV) if(s) s->ref = true; + + for(auto &func : functionV) + if(func && func->name) func->name->ref = true; + + for(auto &scr : scriptV) + if(scr.name.s) scr.name.s->ref = true; + } + + // + // Module::reset + // + void Module::reset() + { + // Unload locally defined functions from env. + for(Function *&func : functionV) + { + if(func && func->module == this) + env->freeFunction(func); + } + + arrImpV.free(); + arrInitV.free(); + arrNameV.free(); + arrSizeV.free(); + codeV.free(); + funcNameV.free(); + functionV.free(); + importV.free(); + jumpV.free(); + jumpMapV.free(); + regImpV.free(); + regInitV.free(); + regNameV.free(); + scrNameV.free(); + scriptV.free(); + stringV.free(); + + isACS0 = false; + loaded = false; + } + + // + // Module::resetStrings + // + void Module::resetStrings() + { + name.s = env->getString(name.s); + + for(auto &s : arrImpV) s = env->getString(s); + for(auto &s : arrNameV) s = env->getString(s); + for(auto &s : funcNameV) s = env->getString(s); + for(auto &s : regImpV) s = env->getString(s); + for(auto &s : regNameV) s = env->getString(s); + for(auto &s : scrNameV) s = env->getString(s); + for(auto &s : stringV) s = env->getString(s); + + for(auto &func : functionV) + if(func) func->name = env->getString(func->name); + + for(auto &scr : scriptV) + scr.name.s = env->getString(scr.name.s); + } +} + + +// EOF + diff --git a/src/acs/vm/ACSVM/Module.hpp b/src/acs/vm/ACSVM/Module.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b2b46e6132443842ea8d9165f9a76e9d9378e305 --- /dev/null +++ b/src/acs/vm/ACSVM/Module.hpp @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Module class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Module_H__ +#define ACSVM__Module_H__ + +#include "ID.hpp" +#include "List.hpp" +#include "Vector.hpp" + +#include <functional> +#include <memory> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ModuleName + // + // Stores a Module's name. Name semantics are user-defined and must provide + // a (user-defined) mapping from name to bytecode data. The names are used + // internally only for determining if a specific module has already been + // loaded. That is, two ModuleNames should compare equal if and only if they + // designate the same bytecode data. + // + class ModuleName + { + public: + ModuleName(String *s_, void *p_, std::size_t i_) : s{s_}, p{p_}, i{i_} {} + + bool operator == (ModuleName const &name) const + {return s == name.s && p == name.p && i == name.i;} + bool operator != (ModuleName const &name) const + {return s != name.s || p != name.p || i != name.i;} + + std::size_t hash() const; + + // String value. May be null. + String *s; + + // Arbitrary pointer value. + void *p; + + // Arbitrary integer value. + std::size_t i; + }; + + // + // Module + // + // Represents an ACS bytecode module. + // + class Module + { + public: + Module(Environment *env, ModuleName const &name); + ~Module(); + + void readBytecode(Byte const *data, std::size_t size); + + void refStrings() const; + + void reset(); + + void resetStrings(); + + Environment *env; + ModuleName name; + + Vector<String *> arrImpV; + Vector<ArrayInit> arrInitV; + Vector<String *> arrNameV; + Vector<Word> arrSizeV; + Vector<Word> codeV; + Vector<String *> funcNameV; + Vector<Function *> functionV; + Vector<Module *> importV; + Vector<Jump> jumpV; + Vector<JumpMap> jumpMapV; + Vector<String *> regImpV; + Vector<WordInit> regInitV; + Vector<String *> regNameV; + Vector<String *> scrNameV; + Vector<Script> scriptV; + Vector<String *> stringV; + + ListLink<Module> hashLink; + + bool isACS0; + bool loaded; + + + static std::pair< + std::unique_ptr<Byte[]> /*data*/, + std::size_t /*size*/> + DecryptStringACSE(Byte const *data, std::size_t size, std::size_t iter); + + static std::unique_ptr<char[]> ParseStringACS0(Byte const *first, + Byte const *last, std::size_t len); + + static std::tuple< + Byte const * /*begin*/, + Byte const * /*end*/, + std::size_t /*len*/> + ScanStringACS0(Byte const *data, std::size_t size, std::size_t iter); + + private: + bool chunkIterACSE(Byte const *data, std::size_t size, + bool (Module::*chunker)(Byte const *, std::size_t, Word)); + + void chunkStrTabACSE(Vector<String *> &strV, + Byte const *data, std::size_t size, bool junk); + + bool chunkerACSE_AIMP(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_AINI(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_ARAY(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_ASTR(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_ATAG(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_FARY(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_FNAM(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_FUNC(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_JUMP(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_LOAD(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_MEXP(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_MIMP(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_MINI(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_MSTR(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SARY(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SFLG(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SNAM(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SPTR8(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SPTR12(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_STRE(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_STRL(Byte const *data, std::size_t size, Word chunkName); + bool chunkerACSE_SVCT(Byte const *data, std::size_t size, Word chunkName); + + void readBytecodeACS0(Byte const *data, std::size_t size); + void readBytecodeACSE(Byte const *data, std::size_t size, + bool compressed, std::size_t iter = 4); + + void readChunksACSE(Byte const *data, std::size_t size, bool fakeACS0); + + void readCodeACS0(Byte const *data, std::size_t size, bool compressed); + + String *readStringACS0(Byte const *data, std::size_t size, std::size_t iter); + + void setScriptNameTypeACSE(Script *scr, Word nameInt, Word type); + }; +} + +namespace std +{ + // + // hash<::ACSVM::ModuleName> + // + template<> + struct hash<::ACSVM::ModuleName> + { + size_t operator () (::ACSVM::ModuleName const &name) const + {return name.hash();} + }; +} + +#endif//ACSVM__Module_H__ + diff --git a/src/acs/vm/ACSVM/ModuleACS0.cpp b/src/acs/vm/ACSVM/ModuleACS0.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ae16ac2f2efd1ce0bd4d16b1a8a3f4342984907 --- /dev/null +++ b/src/acs/vm/ACSVM/ModuleACS0.cpp @@ -0,0 +1,333 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Module class bytecode reading. +// +//----------------------------------------------------------------------------- + +#include "Module.hpp" + +#include "BinaryIO.hpp" +#include "Environment.hpp" +#include "Error.hpp" +#include "Jump.hpp" +#include "Script.hpp" +#include "Tracer.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Module::reaBytecode + // + void Module::readBytecode(Byte const *data, std::size_t size) + { + try + { + if(size < 4) throw ReadError(); + + switch(ReadLE4(data)) + { + case MakeID("ACS\0"): + readBytecodeACS0(data, size); + break; + + case MakeID("ACSE"): + readBytecodeACSE(data, size, false); + break; + + case MakeID("ACSe"): + readBytecodeACSE(data, size, true); + break; + } + } + catch(...) + { + // If an exception occurs before module is fully loaded, reset it. + if(!loaded) + reset(); + + throw; + } + } + + // + // Module::readBytecodeACS0 + // + void Module::readBytecodeACS0(Byte const *data, std::size_t size) + { + std::size_t iter; + + // Read table index. + if(size < 8) throw ReadError(); + iter = ReadLE4(data + 4); + if(iter > size) throw ReadError(); + + // Check for ACSE header behind indicated table. + if(iter >= 8) + { + switch(ReadLE4(data + (iter - 4))) + { + case MakeID("ACSE"): + return readBytecodeACSE(data, size, false, iter - 8); + + case MakeID("ACSe"): + return readBytecodeACSE(data, size, true, iter - 8); + } + } + + // Mark as ACS0. + isACS0 = true; + + // Read script table. + + // Read script count. + if(size - iter < 4) throw ReadError(); + scriptV.alloc(ReadLE4(data + iter), this); iter += 4; + + // Read scripts. + if(size - iter < scriptV.size() * 12) throw ReadError(); + for(Script &scr : scriptV) + { + scr.name.i = ReadLE4(data + iter); iter += 4; + scr.codeIdx = ReadLE4(data + iter); iter += 4; + scr.argC = ReadLE4(data + iter); iter += 4; + + std::tie(scr.type, scr.name.i) = env->getScriptTypeACS0(scr.name.i); + } + + // Read string table. + + // Read string count. + if(size - iter < 4) throw ReadError(); + stringV.alloc(ReadLE4(data + iter)); iter += 4; + + // Read strings. + if(size - iter < stringV.size() * 4) throw ReadError(); + for(String *&str : stringV) + { + str = readStringACS0(data, size, ReadLE4(data + iter)); iter += 4; + } + + // Read code. + readCodeACS0(data, size, false); + + loaded = true; + } + + // + // Module::readCodeACS0 + // + void Module::readCodeACS0(Byte const *data, std::size_t size, bool compressed) + { + TracerACS0 tracer{env, data, size, compressed}; + + // Trace code paths from this module. + tracer.trace(this); + + codeV.alloc(tracer.codeC); + jumpMapV.alloc(tracer.jumpMapC); + + tracer.translate(this); + } + + // + // Module::readStringACS0 + // + String *Module::readStringACS0(Byte const *data, std::size_t size, std::size_t iter) + { + Byte const *begin, *end; + std::size_t len; + std::tie(begin, end, len) = ScanStringACS0(data, size, iter); + + // If result length is same as input length, no processing is needed. + if(static_cast<std::size_t>(end - begin) == len) + { + // Byte is always unsigned char, which is allowed to alias with char. + return env->getString( + reinterpret_cast<char const *>(begin), + reinterpret_cast<char const *>(end)); + } + else + return env->getString(ParseStringACS0(begin, end, len).get(), len); + } + + // + // Module::ParseStringACS0 + // + std::unique_ptr<char[]> Module::ParseStringACS0(Byte const *first, + Byte const *last, std::size_t len) + { + std::unique_ptr<char[]> buf{new char[len + 1]}; + char *bufItr = buf.get(); + + for(Byte const *s = first; s != last;) + { + if(*s == '\\') + { + if(++s == last) + break; + + switch(*s) + { + case 'a': *bufItr++ += '\a'; ++s; break; + case 'b': *bufItr++ += '\b'; ++s; break; + case 'c': *bufItr++ += '\x1C'; ++s; break; // ZDoom color escape + case 'f': *bufItr++ += '\f'; ++s; break; + case 'r': *bufItr++ += '\r'; ++s; break; + case 'n': *bufItr++ += '\n'; ++s; break; + case 't': *bufItr++ += '\t'; ++s; break; + case 'v': *bufItr++ += '\v'; ++s; break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + for(unsigned int i = 3, c = 0; i-- && s != last; ++s) + { + switch(*s) + { + case '0': c = c * 8 + 00; continue; + case '1': c = c * 8 + 01; continue; + case '2': c = c * 8 + 02; continue; + case '3': c = c * 8 + 03; continue; + case '4': c = c * 8 + 04; continue; + case '5': c = c * 8 + 05; continue; + case '6': c = c * 8 + 06; continue; + case '7': c = c * 8 + 07; continue; + } + + *bufItr++ = c; + break; + } + break; + + case 'X': case 'x': + ++s; + for(unsigned int i = 2, c = 0; i-- && s != last; ++s) + { + switch(*s) + { + case '0': c = c * 16 + 0x0; continue; + case '1': c = c * 16 + 0x1; continue; + case '2': c = c * 16 + 0x2; continue; + case '3': c = c * 16 + 0x3; continue; + case '4': c = c * 16 + 0x4; continue; + case '5': c = c * 16 + 0x5; continue; + case '6': c = c * 16 + 0x6; continue; + case '7': c = c * 16 + 0x7; continue; + case '8': c = c * 16 + 0x8; continue; + case '9': c = c * 16 + 0x9; continue; + case 'A': c = c * 16 + 0xA; continue; + case 'B': c = c * 16 + 0xB; continue; + case 'C': c = c * 16 + 0xC; continue; + case 'D': c = c * 16 + 0xD; continue; + case 'E': c = c * 16 + 0xE; continue; + case 'F': c = c * 16 + 0xF; continue; + case 'a': c = c * 16 + 0xa; continue; + case 'b': c = c * 16 + 0xb; continue; + case 'c': c = c * 16 + 0xc; continue; + case 'd': c = c * 16 + 0xd; continue; + case 'e': c = c * 16 + 0xe; continue; + case 'f': c = c * 16 + 0xf; continue; + } + + *bufItr++ = c; + break; + } + break; + + default: + *bufItr++ = *s++; + break; + } + } + else + *bufItr++ = *s++; + } + + *bufItr++ = '\0'; + + return buf; + } + + // + // Module::ScanStringACS0 + // + std::tuple< + Byte const * /*begin*/, + Byte const * /*end*/, + std::size_t /*len*/> + Module::ScanStringACS0(Byte const *data, std::size_t size, std::size_t iter) + { + if(iter > size) throw ReadError(); + + Byte const *begin = data + iter; + Byte const *end = data + size; + Byte const *s = begin; + std::size_t len = 0; + + while(s != end && *s) + { + if(*s++ == '\\') + { + if(s == end || !*s) + break; + + switch(*s++) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + for(int i = 2; i-- && s != end; ++s) + { + switch(*s) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + continue; + } + + break; + } + break; + + case 'X': case 'x': + for(int i = 2; i-- && s != end; ++s) + { + switch(*s) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + continue; + } + + break; + } + break; + + default: + break; + } + } + + ++len; + } + + // If not terminated by a null, string is malformed. + if(s == end) throw ReadError(); + + return std::make_tuple(begin, s, len); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/ModuleACSE.cpp b/src/acs/vm/ACSVM/ModuleACSE.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a38a29f5be3f26aa8652f1d6fdfedd7c997883e1 --- /dev/null +++ b/src/acs/vm/ACSVM/ModuleACSE.cpp @@ -0,0 +1,860 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Module class bytecode reading. +// +//----------------------------------------------------------------------------- + +#include "Module.hpp" + +#include "Array.hpp" +#include "BinaryIO.hpp" +#include "Environment.hpp" +#include "Error.hpp" +#include "Function.hpp" +#include "Init.hpp" +#include "Jump.hpp" +#include "Script.hpp" + +#include <algorithm> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Module::chunkIterACSE + // + bool Module::chunkIterACSE(Byte const *data, std::size_t size, + bool (Module::*chunker)(Byte const *, std::size_t, Word)) + { + std::size_t iter = 0; + + while(iter != size) + { + // Need space for header. + if(size - iter < 8) throw ReadError(); + + // Read header. + Word chunkName = ReadLE4(data + iter + 0); + Word chunkSize = ReadLE4(data + iter + 4); + + // Consume header. + iter += 8; + + // Need space for payload. + if(size - iter < chunkSize) throw ReadError(); + + // Read payload. + if((this->*chunker)(data + iter, chunkSize, chunkName)) + return true; + + // Consume payload. + iter += chunkSize; + } + + return false; + } + + // + // Module::chunkStrTabACSE + // + void Module::chunkStrTabACSE(Vector<String *> &strV, + Byte const *data, std::size_t size, bool junk) + { + std::size_t iter = 0; + + if(junk) + { + if(size < 12) throw ReadError(); + + /*junk = ReadLE4(data + iter);*/ iter += 4; + strV.alloc(ReadLE4(data + iter)); iter += 4; + /*junk = ReadLE4(data + iter);*/ iter += 4; + } + else + { + if(size < 4) throw ReadError(); + + strV.alloc(ReadLE4(data + iter)); iter += 4; + } + + if(size - iter < strV.size() * 4) throw ReadError(); + for(String *&str : strV) + { + str = readStringACS0(data, size, ReadLE4(data + iter)); iter += 4; + } + } + + // + // Module::chunkerACSE_AIMP + // + bool Module::chunkerACSE_AIMP(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("AIMP")) return false; + + if(size < 4) throw ReadError(); + + // Chunk starts with a number of entries. However, that is redundant with + // just checking for the end of the chunk as in MIMP, so do that. + + // Determine highest index. + Word arrC = 0; + for(std::size_t iter = 4; iter != size;) + { + if(size - iter < 8) throw ReadError(); + + Word idx = ReadLE4(data + iter); iter += 4; + /* len = LeadLE4(data + iter);*/ iter += 4; + + arrC = std::max<Word>(arrC, idx + 1); + + Byte const *next; + std::tie(std::ignore, next, std::ignore) = ScanStringACS0(data, size, iter); + + iter = next - data + 1; + } + + // Read imports. + arrImpV.alloc(arrC); + for(std::size_t iter = 4; iter != size;) + { + Word idx = ReadLE4(data + iter); iter += 4; + /* len = LeadLE4(data + iter);*/ iter += 4; + + Byte const *next; + std::size_t len; + std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); + + std::unique_ptr<char[]> str = ParseStringACS0(data + iter, next, len); + + arrImpV[idx] = env->getString(str.get(), len); + + iter = next - data + 1; + } + + return true; + } + + // + // Module::chunkerACSE_AINI + // + bool Module::chunkerACSE_AINI(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("AINI")) return false; + + if(size < 4 || size % 4) throw ReadError(); + + Word idx = ReadLE4(data); + + // Silently ignore out of bounds initializers. + if(idx >= arrInitV.size()) return false; + + auto &init = arrInitV[idx]; + for(std::size_t iter = 4; iter != size; iter += 4) + init.setVal(iter / 4 - 1, ReadLE4(data + iter)); + + return false; + } + + // + // Module::chunkerACSE_ARAY + // + bool Module::chunkerACSE_ARAY(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("ARAY")) return false; + + if(size % 8) throw ReadError(); + + Word arrC = 0; + + // Determine highest index. + for(std::size_t iter = 0; iter != size; iter += 8) + arrC = std::max<Word>(arrC, ReadLE4(data + iter) + 1); + + arrNameV.alloc(arrC); + arrInitV.alloc(arrC); + arrSizeV.alloc(arrC); + + for(std::size_t iter = 0; iter != size;) + { + Word idx = ReadLE4(data + iter); iter += 4; + Word len = ReadLE4(data + iter); iter += 4; + + arrInitV[idx].reserve(len); + arrSizeV[idx] = len; + + // Use names from MEXP. + if(idx < regNameV.size()) + { + arrNameV[idx] = regNameV[idx]; + regNameV[idx] = nullptr; + } + } + + return true; + } + + // + // Module::chunkerACSE_ASTR + // + bool Module::chunkerACSE_ASTR(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("ASTR")) return false; + + if(size % 4) throw ReadError(); + + for(std::size_t iter = 0; iter != size;) + { + Word idx = ReadLE4(data + iter); iter += 4; + + // Silently ignore out of bounds initializers. + if(idx >= arrInitV.size()) continue; + + auto &init = arrInitV[idx]; + for(Word i = 0, e = arrSizeV[idx]; i != e; ++i) + init.setTag(i, InitTag::String); + } + + return false; + } + + // + // Module::chunkerACSE_ATAG + // + bool Module::chunkerACSE_ATAG(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("ATAG")) return false; + + if(size < 5 || data[0]) throw ReadError(); + + Word idx = ReadLE4(data + 1); + + // Silently ignore out of bounds initializers. + if(idx >= arrInitV.size()) return false; + + auto &init = arrInitV[idx]; + for(std::size_t iter = 5; iter != size; ++iter) + { + switch(data[iter]) + { + case 0: init.setTag(iter - 5, InitTag::Integer); break; + case 1: init.setTag(iter - 5, InitTag::String); break; + case 2: init.setTag(iter - 5, InitTag::Function); break; + } + } + + return false; + } + + // + // Module::chunkerACSE_FARY + // + bool Module::chunkerACSE_FARY(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("FARY")) return false; + + if(size < 2 || (size - 2) % 4) throw ReadError(); + + Word idx = ReadLE2(data); + std::size_t arrC = (size - 2) / 4; + + if(idx < functionV.size() && functionV[idx]) + functionV[idx]->locArrC = arrC; + + return false; + } + + // + // Module::chunkerACSE_FNAM + // + bool Module::chunkerACSE_FNAM(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("FNAM")) return false; + + chunkStrTabACSE(funcNameV, data, size, false); + + return true; + } + + // + // Module::chunkerACSE_FUNC + // + bool Module::chunkerACSE_FUNC(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("FUNC")) return false; + + if(size % 8) throw ReadError(); + + // Read functions. + functionV.alloc(size / 8); + + std::size_t iter = 0; + for(Function *&func : functionV) + { + Word idx = iter / 8; + Word argC = ReadLE1(data + iter); iter += 1; + Word locRegC = ReadLE1(data + iter); iter += 1; + Word flags = ReadLE2(data + iter); iter += 2; + Word codeIdx = ReadLE4(data + iter); iter += 4; + + // Ignore undefined functions for now. + if(!codeIdx) continue; + + String *funcName = idx < funcNameV.size() ? funcNameV[idx] : nullptr; + Function *function = env->getFunction(this, funcName); + + function->argC = argC; + function->locRegC = locRegC; + function->flagRet = flags & 0x0001; + function->codeIdx = codeIdx; + + func = function; + } + + return true; + } + + // + // Module::chunkerACSE_JUMP + // + bool Module::chunkerACSE_JUMP(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("JUMP")) return false; + + if(size % 4) throw ReadError(); + + // Read jumps. + jumpV.alloc(size / 4); + + std::size_t iter = 0; + for(Jump &jump : jumpV) + { + jump.codeIdx = ReadLE4(data + iter); iter += 4; + } + + return true; + } + + // + // Module::chunkerACSE_LOAD + // + bool Module::chunkerACSE_LOAD(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("LOAD")) return false; + + // Count imports. + std::size_t importC = 0; + for(Byte const *iter = data, *end = data + size; iter != end; ++ iter) + if(!*iter) ++importC; + + importV.alloc(importC); + + for(std::size_t iter = 0, i = 0; iter != size;) + { + Byte const *next; + std::size_t len; + std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); + + std::unique_ptr<char[]> str = ParseStringACS0(data + iter, next, len); + + auto loadName = env->getModuleName(str.get(), len); + if(loadName != name) + importV[i++] = env->getModule(std::move(loadName)); + else + importV[i++] = this; + + iter = next - data + 1; + } + + return true; + } + + // + // Module::chunkerACSE_MEXP + // + bool Module::chunkerACSE_MEXP(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("MEXP")) return false; + + chunkStrTabACSE(regNameV, data, size, false); + + return true; + } + + // + // Module::chunkerACSE_MIMP + // + bool Module::chunkerACSE_MIMP(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("MIMP")) return false; + + // Determine highest index. + Word regC = 0; + for(std::size_t iter = 0; iter != size;) + { + if(size - iter < 4) throw ReadError(); + + Word idx = ReadLE4(data + iter); iter += 4; + + regC = std::max<Word>(regC, idx + 1); + + Byte const *next; + std::tie(std::ignore, next, std::ignore) = ScanStringACS0(data, size, iter); + + iter = next - data + 1; + } + + // Read imports. + regImpV.alloc(regC); + for(std::size_t iter = 0; iter != size;) + { + Word idx = ReadLE4(data + iter); iter += 4; + + Byte const *next; + std::size_t len; + std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); + + std::unique_ptr<char[]> str = ParseStringACS0(data + iter, next, len); + + regImpV[idx] = env->getString(str.get(), len); + + iter = next - data + 1; + } + + return true; + } + + // + // Module::chunkerACSE_MINI + // + bool Module::chunkerACSE_MINI(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("MINI")) return false; + + if(size % 4 || size < 4) throw ReadError("bad MINI size"); + + Word idx = ReadLE4(data); + Word regC = idx + size / 4 - 1; + + if(regC > regInitV.size()) + regInitV.realloc(regC); + + for(std::size_t iter = 4; iter != size; iter += 4) + regInitV[idx++] = ReadLE4(data + iter); + + return true; + } + + // + // Module::chunkerACSE_MSTR + // + bool Module::chunkerACSE_MSTR(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("MSTR")) return false; + + if(size % 4) throw ReadError(); + + for(std::size_t iter = 0; iter != size;) + { + Word idx = ReadLE4(data + iter); iter += 4; + + // Silently ignore out of bounds initializers. + if(idx < regInitV.size()) + regInitV[idx].tag = InitTag::String; + } + + return false; + } + + // + // Module::chunkerACSE_SARY + // + bool Module::chunkerACSE_SARY(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SARY")) return false; + + if(size < 2 || (size - 2) % 4) throw ReadError(); + + Word nameInt = ReadLE2(data); + std::size_t arrC = (size - 2) / 4; + + if(nameInt & 0x8000) nameInt |= 0xFFFF0000; + + for(Script &scr : scriptV) + if(scr.name.i == nameInt) scr.locArrC = arrC; + + return false; + } + + // + // Module::chunkerACSE_SFLG + // + bool Module::chunkerACSE_SFLG(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SFLG")) return false; + + if(size % 4) throw ReadError(); + + for(std::size_t iter = 0; iter != size;) + { + Word nameInt = ReadLE2(data + iter); iter += 2; + Word flags = ReadLE2(data + iter); iter += 2; + + bool flagNet = !!(flags & 0x0001); + bool flagClient = !!(flags & 0x0002); + + if(nameInt & 0x8000) nameInt |= 0xFFFF0000; + + for(Script &scr : scriptV) + { + if(scr.name.i == nameInt) + { + scr.flagClient = flagClient; + scr.flagNet = flagNet; + } + } + } + + return false; + } + + // + // Module::chunkerACSE_SNAM + // + bool Module::chunkerACSE_SNAM(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SNAM")) return false; + + chunkStrTabACSE(scrNameV, data, size, false); + + return true; + } + + // + // Module::chunkerACSE_SPTR8 + // + // Reads 8-byte SPTR chunk. + // + bool Module::chunkerACSE_SPTR8(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SPTR")) return false; + + if(size % 8) throw ReadError(); + + // Read scripts. + scriptV.alloc(size / 8, this); + + std::size_t iter = 0; + for(Script &scr : scriptV) + { + Word nameInt = ReadLE2(data + iter); iter += 2; + Word type = ReadLE1(data + iter); iter += 1; + scr.argC = ReadLE1(data + iter); iter += 1; + scr.codeIdx = ReadLE4(data + iter); iter += 4; + + if(nameInt & 0x8000) nameInt |= 0xFFFF0000; + setScriptNameTypeACSE(&scr, nameInt, type); + } + + return true; + } + + // + // Module::chunkerACSE_SPTR12 + // + // Reads 12-byte SPTR chunk. + // + bool Module::chunkerACSE_SPTR12(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SPTR")) return false; + + if(size % 12) throw ReadError(); + + // Read scripts. + scriptV.alloc(size / 12, this); + + std::size_t iter = 0; + for(Script &scr : scriptV) + { + Word nameInt = ReadLE2(data + iter); iter += 2; + Word type = ReadLE2(data + iter); iter += 2; + scr.codeIdx = ReadLE4(data + iter); iter += 4; + scr.argC = ReadLE4(data + iter); iter += 4; + + if(nameInt & 0x8000) nameInt |= 0xFFFF0000; + setScriptNameTypeACSE(&scr, nameInt, type); + } + + return true; + } + + // + // Module::chunkerACSE_STRE + // + bool Module::chunkerACSE_STRE(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("STRE")) return false; + + std::size_t iter = 0; + + if(size < 12) throw ReadError(); + + /*junk = ReadLE4(data + iter);*/ iter += 4; + stringV.alloc(ReadLE4(data + iter)); iter += 4; + /*junk = ReadLE4(data + iter);*/ iter += 4; + + if(size - iter < stringV.size() * 4) throw ReadError(); + for(String *&str : stringV) + { + std::size_t offset = ReadLE4(data + iter); iter += 4; + + // Decrypt string. + std::unique_ptr<Byte[]> buf; + std::size_t len; + std::tie(buf, len) = DecryptStringACSE(data, size, offset); + + // Scan string. + Byte const *bufEnd; + std::tie(std::ignore, bufEnd, len) = ScanStringACS0(buf.get(), len, 0); + + // Parse string. + str = env->getString(ParseStringACS0(buf.get(), bufEnd, len).get(), len); + } + + return true; + } + + // + // Module::chunkerACSE_STRL + // + bool Module::chunkerACSE_STRL(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("STRL")) return false; + + chunkStrTabACSE(stringV, data, size, true); + + return true; + } + + // + // Module::chunkerACSE_SVCT + // + bool Module::chunkerACSE_SVCT(Byte const *data, std::size_t size, Word chunkName) + { + if(chunkName != MakeID("SVCT")) return false; + + if(size % 4) throw ReadError(); + + for(std::size_t iter = 0; iter != size;) + { + Word nameInt = ReadLE2(data + iter); iter += 2; + Word regC = ReadLE2(data + iter); iter += 2; + + if(nameInt & 0x8000) nameInt |= 0xFFFF0000; + + for(Script &scr : scriptV) + { + if(scr.name.i == nameInt) + scr.locRegC = regC; + } + } + + return false; + } + + // + // Module::readBytecodeACSE + // + void Module::readBytecodeACSE(Byte const *data, std::size_t size, + bool compressed, std::size_t offset) + { + std::size_t iter = offset; + + // Find table start. + if(iter > size || size - iter < 4) throw ReadError(); + iter = ReadLE4(data + iter); + if(iter > size) throw ReadError(); + + // Read chunks. + if(offset == 4) + { + readChunksACSE(data + iter, size - iter, false); + } + else + { + if(iter <= offset) + readChunksACSE(data + iter, offset - iter, true); + else + readChunksACSE(data + iter, size - iter, true); + } + + // Read code. + readCodeACS0(data, size, compressed); + + loaded = true; + } + + // + // Module::readChunksACSE + // + void Module::readChunksACSE(Byte const *data, std::size_t size, bool fakeACS0) + { + // MEXP - Module Variable/Array Export + chunkIterACSE(data, size, &Module::chunkerACSE_MEXP); + + // ARAY - Module Arrays + chunkIterACSE(data, size, &Module::chunkerACSE_ARAY); + + // AINI - Module Array Init + chunkIterACSE(data, size, &Module::chunkerACSE_AINI); + + // FNAM - Function Names + chunkIterACSE(data, size, &Module::chunkerACSE_FNAM); + + // FUNC - Functions + chunkIterACSE(data, size, &Module::chunkerACSE_FUNC); + + // FARY - Function Arrays + chunkIterACSE(data, size, &Module::chunkerACSE_FARY); + + // JUMP - Dynamic Jump Targets + chunkIterACSE(data, size, &Module::chunkerACSE_JUMP); + + // MINI - Module Variable Init + chunkIterACSE(data, size, &Module::chunkerACSE_MINI); + + // SNAM - Script Names + chunkIterACSE(data, size, &Module::chunkerACSE_SNAM); + + // SPTR - Script Pointers + if(fakeACS0) + chunkIterACSE(data, size, &Module::chunkerACSE_SPTR8); + else + chunkIterACSE(data, size, &Module::chunkerACSE_SPTR12); + + // SARY - Script Arrays + chunkIterACSE(data, size, &Module::chunkerACSE_SARY); + + // SFLG - Script Flags + chunkIterACSE(data, size, &Module::chunkerACSE_SFLG); + + // SVCT - Script Variable Count + chunkIterACSE(data, size, &Module::chunkerACSE_SVCT); + + // STRE - Encrypted String Literals + if(!chunkIterACSE(data, size, &Module::chunkerACSE_STRE)) + { + // STRL - String Literals + chunkIterACSE(data, size, &Module::chunkerACSE_STRL); + } + + // LOAD - Library Loading + chunkIterACSE(data, size, &Module::chunkerACSE_LOAD); + + // Process function imports. + for(auto &func : functionV) + { + if(func) continue; + + std::size_t idx = &func - functionV.data(); + + if(idx >= funcNameV.size()) continue; + + auto &funcName = funcNameV[idx]; + + if(!funcName) continue; + + for(auto &import : importV) + { + for(auto &funcImp : import->functionV) + { + if(funcImp && funcImp->name == funcName) + { + func = funcImp; + goto func_found; + } + } + } + + func_found:; + } + + // AIMP - Module Array Import + chunkIterACSE(data, size, &Module::chunkerACSE_AIMP); + + // MIMP - Module Variable Import + chunkIterACSE(data, size, &Module::chunkerACSE_MIMP); + + // ASTR - Module Array Strings + chunkIterACSE(data, size, &Module::chunkerACSE_ASTR); + + // ATAG - Module Array Tagging + chunkIterACSE(data, size, &Module::chunkerACSE_ATAG); + + // MSTR - Module Variable Strings + chunkIterACSE(data, size, &Module::chunkerACSE_MSTR); + + for(auto &init : arrInitV) + init.finish(); + } + + // + // Module::setScriptNameTypeACSE + // + void Module::setScriptNameTypeACSE(Script *scr, Word nameInt, Word type) + { + // If high bit is set, script is named. + if((scr->name.i = nameInt) & 0x80000000) + { + // Fetch name. + Word nameIdx = ~scr->name.i; + if(nameIdx < scrNameV.size()) + scr->name.s = scrNameV[nameIdx]; + } + + scr->type = env->getScriptTypeACSE(type); + } + + // + // Module::DecryptStringACSE + // + std::pair< + std::unique_ptr<Byte[]> /*data*/, + std::size_t /*size*/> + Module::DecryptStringACSE(Byte const *data, std::size_t size, std::size_t iter) + { + Word const key = iter * 157135; + + // Calculate length. Start at 1 for null terminator. + std::size_t len = 1; + for(std::size_t i = iter, n = 0;; ++i, ++n, ++len) + { + if(i == size) throw ReadError(); + + Byte c = static_cast<Byte>(data[i] ^ (n / 2 + key)); + if(!c) break; + } + + // Decrypt data. + std::unique_ptr<Byte[]> buf{new Byte[len]}; + for(std::size_t i = iter, n = 0;; ++i, ++n) + { + Byte c = static_cast<Byte>(data[i] ^ (n / 2 + key)); + if(!(buf[n] = c)) break; + } + + return {std::move(buf), len}; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/PrintBuf.cpp b/src/acs/vm/ACSVM/PrintBuf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..928585433cfc8bc92bca357cc8e6c2cd89bd7de5 --- /dev/null +++ b/src/acs/vm/ACSVM/PrintBuf.cpp @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// PrintBuf class. +// +//----------------------------------------------------------------------------- + +#include "PrintBuf.hpp" + +#include "BinaryIO.hpp" + +#include <cstdio> +#include <cstdlib> +#include <new> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // PrintBuf constructor + // + PrintBuf::PrintBuf() : + buffer{nullptr}, + bufEnd{nullptr}, + bufBeg{nullptr}, + bufPtr{nullptr} + { + } + + // + // PrintBuf destructor + // + PrintBuf::~PrintBuf() + { + std::free(buffer); + } + + // + // PrintBuf::drop + // + void PrintBuf::drop() + { + if(bufBeg != buffer) + { + bufPtr = bufBeg - 4; + bufBeg = bufPtr - ReadLE4(reinterpret_cast<Byte *>(bufPtr)); + } + else + bufPtr = bufBeg; + } + + // + // PrintBuf::format + // + void PrintBuf::format(char const *fmt, ...) + { + va_list arg; + va_start(arg, fmt); + formatv(fmt, arg); + va_end(arg); + } + + // + // PrintBuf::formatv + // + void PrintBuf::formatv(char const *fmt, va_list arg) + { + bufPtr += std::vsprintf(bufPtr, fmt, arg); + } + + // + // PrintBuf::getLoadBuf + // + char *PrintBuf::getLoadBuf(std::size_t countFull, std::size_t count) + { + if(static_cast<std::size_t>(bufEnd - buffer) <= countFull) + { + char *bufNew; + if(!(bufNew = static_cast<char *>(std::realloc(buffer, countFull + 1)))) + throw std::bad_alloc(); + + buffer = bufNew; + bufEnd = buffer + countFull + 1; + } + + bufPtr = buffer + countFull; + bufBeg = bufPtr - count; + + return buffer; + } + + // + // PrintBuf::push + // + void PrintBuf::push() + { + reserve(4); + WriteLE4(reinterpret_cast<Byte *>(bufPtr), bufPtr - bufBeg); + bufBeg = bufPtr += 4; + } + + // + // PrintBuf::reserve + // + void PrintBuf::reserve(std::size_t count) + { + if(static_cast<std::size_t>(bufEnd - bufPtr) > count) + return; + + // Allocate extra to anticipate further reserves. +1 for null. + count = count * 2 + 1; + + std::size_t idxEnd = bufEnd - buffer; + std::size_t idxBeg = bufBeg - buffer; + std::size_t idxPtr = bufPtr - buffer; + + // Check for size overflow. + if(SIZE_MAX - idxEnd < count) + throw std::bad_alloc(); + + // Check that the current segment won't pass the push limit. + if(UINT32_MAX - (idxEnd - idxBeg) < count) + throw std::bad_alloc(); + + idxEnd += count; + + char *bufNew; + if(!(bufNew = static_cast<char *>(std::realloc(buffer, idxEnd)))) + throw std::bad_alloc(); + + buffer = bufNew; + bufEnd = buffer + idxEnd; + bufBeg = buffer + idxBeg; + bufPtr = buffer + idxPtr; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/PrintBuf.hpp b/src/acs/vm/ACSVM/PrintBuf.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8cbf64d4828e6f8613b6093123c070f88a14be34 --- /dev/null +++ b/src/acs/vm/ACSVM/PrintBuf.hpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// PrintBuf class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__PrintBuf_H__ +#define ACSVM__PrintBuf_H__ + +#include "Types.hpp" + +#include <cstdarg> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // PrintBuf + // + class PrintBuf + { + public: + PrintBuf(); + ~PrintBuf(); + + void clear() {bufBeg = bufPtr = buffer;} + + char const *data() const {return *bufPtr = '\0', bufBeg;} + char const *dataFull() const {return buffer;} + + void drop(); + + // Formats using sprintf. Does not reserve space. + void format(char const *fmt, ...); + void formatv(char const *fmt, std::va_list arg); + + // Returns a pointer to count chars to write into. The caller must write + // to the entire returned buffer. Does not reserve space. + char *getBuf(std::size_t count) + {char *s = bufPtr; bufPtr += count; return s;} + + // Prepares the buffer to be deserialized. + char *getLoadBuf(std::size_t countFull, std::size_t count); + + void push(); + + // Writes literal characters. Does not reserve space. + void put(char c) {*bufPtr++ = c;} + void put(char const *s) {while(*s) *bufPtr++ = *s++;} + void put(char const *s, std::size_t n) {while(n--) *bufPtr++ = *s++;} + + // Ensures at least count chars are available for writing into. + void reserve(std::size_t count); + + std::size_t size() const {return bufPtr - bufBeg;} + std::size_t sizeFull() const {return bufPtr - buffer;} + + private: + char *buffer, *bufEnd, *bufBeg, *bufPtr; + }; +} + +#endif//ACSVM__PrintBuf_H__ + diff --git a/src/acs/vm/ACSVM/Scope.cpp b/src/acs/vm/ACSVM/Scope.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f56a7e6fc57528eac81357b31513e834b7cf456 --- /dev/null +++ b/src/acs/vm/ACSVM/Scope.cpp @@ -0,0 +1,1299 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2020 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Scope classes. +// +//----------------------------------------------------------------------------- + +#include "Scope.hpp" + +#include "Action.hpp" +#include "BinaryIO.hpp" +#include "Environment.hpp" +#include "HashMap.hpp" +#include "HashMapFixed.hpp" +#include "Init.hpp" +#include "Module.hpp" +#include "Script.hpp" +#include "Serial.hpp" +#include "Thread.hpp" + +#include <algorithm> +#include <unordered_set> +#include <vector> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // GlobalScope::PrivData + // + struct GlobalScope::PrivData + { + HashMapKeyMem<Word, HubScope, &HubScope::id, &HubScope::hashLink> scopes; + }; + + // + // HubScope::PrivData + // + struct HubScope::PrivData + { + HashMapKeyMem<Word, MapScope, &MapScope::id, &MapScope::hashLink> scopes; + }; + + // + // MapScope::PrivData + // + struct MapScope::PrivData + { + HashMapFixed<Module *, ModuleScope> scopes; + + HashMapFixed<Word, Script *> scriptInt; + HashMapFixed<String *, Script *> scriptStr; + + HashMapFixed<Script *, Thread *> scriptThread; + }; +} + + +//----------------------------------------------------------------------------| +// Extern Objects | +// + +namespace ACSVM +{ + constexpr std::size_t ModuleScope::ArrC; + constexpr std::size_t ModuleScope::RegC; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // GlobalScope constructor + // + GlobalScope::GlobalScope(Environment *env_, Word id_) : + env{env_}, + id {id_}, + + arrV{}, + regV{}, + + hashLink{this}, + + active{false}, + + pd{new PrivData} + { + } + + // + // GlobalScope destructor + // + GlobalScope::~GlobalScope() + { + reset(); + delete pd; + } + + // + // GlobalScope::countActiveThread + // + std::size_t GlobalScope::countActiveThread() const + { + std::size_t n = 0; + + for(auto &scope : pd->scopes) + { + if(scope.active) + n += scope.countActiveThread(); + } + + return n; + } + + // + // GlobalScope::exec + // + void GlobalScope::exec() + { + // Delegate deferred script actions. + for(auto itr = scriptAction.begin(), end = scriptAction.end(); itr != end;) + { + auto scope = pd->scopes.find(itr->id.global); + if(scope && scope->active) + itr++->link.relink(&scope->scriptAction); + else + ++itr; + } + + for(auto &scope : pd->scopes) + { + if(scope.active) + scope.exec(); + } + } + + // + // GlobalScope::freeHubScope + // + void GlobalScope::freeHubScope(HubScope *scope) + { + pd->scopes.unlink(scope); + delete scope; + } + + // + // GlobalScope::getHubScope + // + HubScope *GlobalScope::getHubScope(Word scopeID) + { + if(auto *scope = pd->scopes.find(scopeID)) + return scope; + + auto scope = new HubScope(this, scopeID); + pd->scopes.insert(scope); + return scope; + } + + // + // GlobalScope::hasActiveThread + // + bool GlobalScope::hasActiveThread() const + { + for(auto &scope : pd->scopes) + { + if(scope.active && scope.hasActiveThread()) + return true; + } + + return false; + } + + // + // GlobalScope::loadState + // + void GlobalScope::loadState(Serial &in) + { + reset(); + + in.readSign(Signature::GlobalScope); + + for(auto &arr : arrV) + arr.loadState(in); + + for(auto ® : regV) + reg = ReadVLN<Word>(in); + + env->readScriptActions(in, scriptAction); + + active = in.in->get() != '\0'; + + for(auto n = ReadVLN<std::size_t>(in); n--;) + getHubScope(ReadVLN<Word>(in))->loadState(in); + + in.readSign(~Signature::GlobalScope); + } + + // + // GlobalScope::lockStrings + // + void GlobalScope::lockStrings() const + { + for(auto &arr : arrV) arr.lockStrings(env); + for(auto ® : regV) ++env->getString(reg)->lock; + + for(auto &action : scriptAction) + action.lockStrings(env); + + for(auto &scope : pd->scopes) + scope.lockStrings(); + } + + // + // GlobalScope::refStrings + // + void GlobalScope::refStrings() const + { + for(auto &arr : arrV) arr.refStrings(env); + for(auto ® : regV) env->getString(reg)->ref = true; + + for(auto &action : scriptAction) + action.refStrings(env); + + for(auto &scope : pd->scopes) + scope.refStrings(); + } + + // + // GlobalScope::reset + // + void GlobalScope::reset() + { + while(scriptAction.next->obj) + delete scriptAction.next->obj; + + pd->scopes.free(); + } + + // + // GlobalScope::saveState + // + void GlobalScope::saveState(Serial &out) const + { + out.writeSign(Signature::GlobalScope); + + for(auto &arr : arrV) + arr.saveState(out); + + for(auto ® : regV) + WriteVLN(out, reg); + + env->writeScriptActions(out, scriptAction); + + out.out->put(active ? '\1' : '\0'); + + WriteVLN(out, pd->scopes.size()); + for(auto &scope : pd->scopes) + { + WriteVLN(out, scope.id); + scope.saveState(out); + } + + out.writeSign(~Signature::GlobalScope); + } + + // + // GlobalScope::unlockStrings + // + void GlobalScope::unlockStrings() const + { + for(auto &arr : arrV) arr.unlockStrings(env); + for(auto ® : regV) --env->getString(reg)->lock; + + for(auto &action : scriptAction) + action.unlockStrings(env); + + for(auto &scope : pd->scopes) + scope.unlockStrings(); + } + + // + // HubScope constructor + // + HubScope::HubScope(GlobalScope *global_, Word id_) : + env {global_->env}, + global{global_}, + id {id_}, + + arrV{}, + regV{}, + + hashLink{this}, + + active{false}, + + pd{new PrivData} + { + } + + // + // HubScope destructor + // + HubScope::~HubScope() + { + reset(); + delete pd; + } + + // + // HubScope::countActiveThread + // + std::size_t HubScope::countActiveThread() const + { + std::size_t n = 0; + + for(auto &scope : pd->scopes) + { + if(scope.active) + n += scope.countActiveThread(); + } + + return n; + } + + // + // HubScope::exec + // + void HubScope::exec() + { + // Delegate deferred script actions. + for(auto itr = scriptAction.begin(), end = scriptAction.end(); itr != end;) + { + auto scope = pd->scopes.find(itr->id.global); + if(scope && scope->active) + itr++->link.relink(&scope->scriptAction); + else + ++itr; + } + + for(auto &scope : pd->scopes) + { + if(scope.active) + scope.exec(); + } + } + + // + // HubScope::freeMapScope + // + void HubScope::freeMapScope(MapScope *scope) + { + pd->scopes.unlink(scope); + delete scope; + } + + // + // HubScope::getMapScope + // + MapScope *HubScope::getMapScope(Word scopeID) + { + if(auto *scope = pd->scopes.find(scopeID)) + return scope; + + auto scope = new MapScope(this, scopeID); + pd->scopes.insert(scope); + return scope; + } + + // + // HubScope::hasActiveThread + // + bool HubScope::hasActiveThread() const + { + for(auto &scope : pd->scopes) + { + if(scope.active && scope.hasActiveThread()) + return true; + } + + return false; + } + + // + // HubScope::loadState + // + void HubScope::loadState(Serial &in) + { + reset(); + + in.readSign(Signature::HubScope); + + for(auto &arr : arrV) + arr.loadState(in); + + for(auto ® : regV) + reg = ReadVLN<Word>(in); + + env->readScriptActions(in, scriptAction); + + active = in.in->get() != '\0'; + + for(auto n = ReadVLN<std::size_t>(in); n--;) + getMapScope(ReadVLN<Word>(in))->loadState(in); + + in.readSign(~Signature::HubScope); + } + + // + // HubScope::lockStrings + // + void HubScope::lockStrings() const + { + for(auto &arr : arrV) arr.lockStrings(env); + for(auto ® : regV) ++env->getString(reg)->lock; + + for(auto &action : scriptAction) + action.lockStrings(env); + + for(auto &scope : pd->scopes) + scope.lockStrings(); + } + + // + // HubScope::refStrings + // + void HubScope::refStrings() const + { + for(auto &arr : arrV) arr.refStrings(env); + for(auto ® : regV) env->getString(reg)->ref = true; + + for(auto &action : scriptAction) + action.refStrings(env); + + for(auto &scope : pd->scopes) + scope.refStrings(); + } + + // + // HubScope::reset + // + void HubScope::reset() + { + while(scriptAction.next->obj) + delete scriptAction.next->obj; + + pd->scopes.free(); + } + + // + // HubScope::saveState + // + void HubScope::saveState(Serial &out) const + { + out.writeSign(Signature::HubScope); + + for(auto &arr : arrV) + arr.saveState(out); + + for(auto ® : regV) + WriteVLN(out, reg); + + env->writeScriptActions(out, scriptAction); + + out.out->put(active ? '\1' : '\0'); + + WriteVLN(out, pd->scopes.size()); + for(auto &scope : pd->scopes) + { + WriteVLN(out, scope.id); + scope.saveState(out); + } + + out.writeSign(~Signature::HubScope); + } + + // + // HubScope::unlockStrings + // + void HubScope::unlockStrings() const + { + for(auto &arr : arrV) arr.unlockStrings(env); + for(auto ® : regV) --env->getString(reg)->lock; + + for(auto &action : scriptAction) + action.unlockStrings(env); + + for(auto &scope : pd->scopes) + scope.unlockStrings(); + } + + // + // MapScope constructor + // + MapScope::MapScope(HubScope *hub_, Word id_) : + env{hub_->env}, + hub{hub_}, + id {id_}, + + hashLink{this}, + + module0{nullptr}, + + active {false}, + clampCallSpec{false}, + + pd{new PrivData} + { + } + + // + // MapScope destructor + // + MapScope::~MapScope() + { + reset(); + delete pd; + } + + // + // MapScope::addModules + // + void MapScope::addModules(Module *const *moduleV, std::size_t moduleC) + { + module0 = moduleC ? moduleV[0] : nullptr; + + // Find all associated modules. + + struct + { + std::unordered_set<Module *> set; + std::vector<Module *> vec; + + void add(Module *module) + { + if(!set.insert(module).second) return; + + vec.push_back(module); + for(auto &import : module->importV) + add(import); + } + } modules; + + for(auto itr = moduleV, end = itr + moduleC; itr != end; ++itr) + modules.add(*itr); + + // Count scripts. + + std::size_t scriptThrC = 0; + std::size_t scriptIntC = 0; + std::size_t scriptStrC = 0; + + for(auto &module : modules.vec) + { + for(auto &script : module->scriptV) + { + ++scriptThrC; + if(script.name.s) + ++scriptStrC; + else + ++scriptIntC; + } + } + + // Create lookup tables. + + pd->scopes.alloc(modules.vec.size()); + pd->scriptInt.alloc(scriptIntC); + pd->scriptStr.alloc(scriptStrC); + pd->scriptThread.alloc(scriptThrC); + + auto scopeItr = pd->scopes.begin(); + auto scriptIntItr = pd->scriptInt.begin(); + auto scriptStrItr = pd->scriptStr.begin(); + auto scriptThrItr = pd->scriptThread.begin(); + + for(auto &module : modules.vec) + { + using ElemScope = HashMapFixed<Module *, ModuleScope>::Elem; + + new(scopeItr++) ElemScope{module, {this, module}, nullptr}; + + for(auto &script : module->scriptV) + { + using ElemInt = HashMapFixed<Word, Script *>::Elem; + using ElemStr = HashMapFixed<String *, Script *>::Elem; + using ElemThr = HashMapFixed<Script *, Thread *>::Elem; + + new(scriptThrItr++) ElemThr{&script, nullptr, nullptr}; + + if(script.name.s) + new(scriptStrItr++) ElemStr{script.name.s, &script, nullptr}; + else + new(scriptIntItr++) ElemInt{script.name.i, &script, nullptr}; + } + } + + pd->scopes.build(); + pd->scriptInt.build(); + pd->scriptStr.build(); + pd->scriptThread.build(); + + for(auto &scope : pd->scopes) + scope.val.import(); + } + + // + // MapScope::countActiveThread + // + std::size_t MapScope::countActiveThread() const + { + return threadActive.size(); + } + + // + // MapScope::exec + // + void MapScope::exec() + { + // Execute deferred script actions. + while(scriptAction.next->obj) + { + ScriptAction *action = scriptAction.next->obj; + Script *script = findScript(action->name); + + if(script) switch(action->action) + { + case ScriptAction::Start: + scriptStart(script, {action->argV.data(), action->argV.size()}); + break; + + case ScriptAction::StartForced: + scriptStartForced(script, {action->argV.data(), action->argV.size()}); + break; + + case ScriptAction::Stop: + scriptStop(script); + break; + + case ScriptAction::Pause: + scriptPause(script); + break; + } + + delete action; + } + + // Execute running threads. + for(auto itr = threadActive.begin(), end = threadActive.end(); itr != end;) + { + itr->exec(); + if(itr->state == ThreadState::Inactive) + freeThread(&*itr++); + else + ++itr; + } + } + + // + // MapScope::findScript + // + Script *MapScope::findScript(ScriptName name) + { + return name.s ? findScript(name.s) : findScript(name.i); + } + + // + // MapScope::findScript + // + Script *MapScope::findScript(String *name) + { + if(Script **script = pd->scriptStr.find(name)) + return *script; + else + return nullptr; + } + + // + // MapScope::findScript + // + Script *MapScope::findScript(Word name) + { + if(Script **script = pd->scriptInt.find(name)) + return *script; + else + return nullptr; + } + + // + // MapScope::freeThread + // + void MapScope::freeThread(Thread *thread) + { + auto itr = pd->scriptThread.find(thread->script); + if(itr && *itr == thread) + *itr = nullptr; + + env->freeThread(thread); + } + + // + // MapScope::getModuleScope + // + ModuleScope *MapScope::getModuleScope(Module *module) + { + return pd->scopes.find(module); + } + + // + // MapScope::getString + // + String *MapScope::getString(Word idx) const + { + if(idx & 0x80000000) + return &env->stringTable[~idx]; + + if(!module0 || idx >= module0->stringV.size()) + return &env->stringTable.getNone(); + + return module0->stringV[idx]; + } + + // + // MapScope::hasActiveThread + // + bool MapScope::hasActiveThread() const + { + for(auto &thread : threadActive) + { + if(thread.state != ThreadState::Inactive) + return true; + } + + return false; + } + + // + // MapScope::hasModules + // + bool MapScope::hasModules() const + { + return !pd->scopes.empty(); + } + + // + // MapScope::isScriptActive + // + bool MapScope::isScriptActive(Script *script) + { + auto itr = pd->scriptThread.find(script); + return itr && *itr && (*itr)->state != ThreadState::Inactive; + } + + // + // MapScope::loadModules + // + void MapScope::loadModules(Serial &in) + { + auto count = ReadVLN<std::size_t>(in); + std::vector<Module *> modules; + modules.reserve(count); + + for(auto n = count; n--;) + modules.emplace_back(env->getModule(env->readModuleName(in))); + + addModules(modules.data(), modules.size()); + + for(auto &module : modules) + pd->scopes.find(module)->loadState(in); + } + + // + // MapScope::loadState + // + void MapScope::loadState(Serial &in) + { + reset(); + + in.readSign(Signature::MapScope); + + env->readScriptActions(in, scriptAction); + active = in.in->get() != '\0'; + loadModules(in); + loadThreads(in); + + in.readSign(~Signature::MapScope); + } + + // + // MapScope::loadThreads + // + void MapScope::loadThreads(Serial &in) + { + for(auto n = ReadVLN<std::size_t>(in); n--;) + { + Thread *thread = env->getFreeThread(); + thread->link.insert(&threadActive); + thread->loadState(in); + + if(in.in->get()) + { + auto scrThread = pd->scriptThread.find(thread->script); + if(scrThread) + *scrThread = thread; + } + } + } + + // + // MapScope::lockStrings + // + void MapScope::lockStrings() const + { + for(auto &action : scriptAction) + action.lockStrings(env); + + for(auto &scope : pd->scopes) + scope.val.lockStrings(); + + for(auto &thread : threadActive) + thread.lockStrings(); + } + + // + // MapScope::refStrings + // + void MapScope::refStrings() const + { + for(auto &action : scriptAction) + action.refStrings(env); + + for(auto &scope : pd->scopes) + scope.val.refStrings(); + + for(auto &thread : threadActive) + thread.refStrings(); + } + + // + // MapScope::reset + // + void MapScope::reset() + { + // Stop any remaining threads and return them to free list. + while(threadActive.next->obj) + { + threadActive.next->obj->stop(); + env->freeThread(threadActive.next->obj); + } + + while(scriptAction.next->obj) + delete scriptAction.next->obj; + + active = false; + + pd->scopes.free(); + + pd->scriptInt.free(); + pd->scriptStr.free(); + pd->scriptThread.free(); + } + + // + // MapScope::saveModules + // + void MapScope::saveModules(Serial &out) const + { + WriteVLN(out, pd->scopes.size()); + + for(auto &scope : pd->scopes) + env->writeModuleName(out, scope.key->name); + + for(auto &scope : pd->scopes) + scope.val.saveState(out); + } + + // + // MapScope::saveState + // + void MapScope::saveState(Serial &out) const + { + out.writeSign(Signature::MapScope); + + env->writeScriptActions(out, scriptAction); + out.out->put(active ? '\1' : '\0'); + saveModules(out); + saveThreads(out); + + out.writeSign(~Signature::MapScope); + } + + // + // MapScope::saveThreads + // + void MapScope::saveThreads(Serial &out) const + { + WriteVLN(out, threadActive.size()); + for(auto &thread : threadActive) + { + thread.saveState(out); + + auto scrThread = pd->scriptThread.find(thread.script); + out.out->put(scrThread && *scrThread == &thread ? '\1' : '\0'); + } + } + + // + // MapScope::scriptPause + // + bool MapScope::scriptPause(Script *script) + { + auto itr = pd->scriptThread.find(script); + if(!itr || !*itr) + return false; + + switch((*itr)->state.state) + { + case ThreadState::Inactive: + case ThreadState::Paused: + case ThreadState::Stopped: + return false; + + default: + (*itr)->state = ThreadState::Paused; + return true; + } + } + + // + // MapScope::scriptPause + // + bool MapScope::scriptPause(ScriptName name, ScopeID scope) + { + if(scope != ScopeID{hub->global->id, hub->id, id}) + { + env->deferAction({scope, name, ScriptAction::Pause, {}}); + return true; + } + + if(Script *script = findScript(name)) + return scriptPause(script); + else + return false; + } + + // + // MapScope::scriptStart + // + bool MapScope::scriptStart(Script *script, ScriptStartInfo const &info) + { + auto itr = pd->scriptThread.find(script); + if(!itr) + return false; + + if(Thread *&thread = *itr) + { + switch(thread->state.state) + { + case ThreadState::Paused: + thread->state = ThreadState::Running; + return true; + + default: + return false; + } + } + else + { + thread = env->getFreeThread(); + thread->start(script, this, info.info, info.argV, info.argC); + if(info.func) info.func(thread); + if(info.funcc) info.funcc(thread); + return true; + } + } + + // + // MapScope::scriptStart + // + bool MapScope::scriptStart(ScriptName name, ScopeID scope, ScriptStartInfo const &info) + { + if(scope != ScopeID{hub->global->id, hub->id, id}) + { + env->deferAction({scope, name, ScriptAction::Start, {info.argV, info.argC}}); + return true; + } + + if(Script *script = findScript(name)) + return scriptStart(script, info); + else + return false; + } + + // + // MapScope::scriptStartForced + // + bool MapScope::scriptStartForced(Script *script, ScriptStartInfo const &info) + { + Thread *thread = env->getFreeThread(); + + thread->start(script, this, info.info, info.argV, info.argC); + if(info.func) info.func(thread); + if(info.funcc) info.funcc(thread); + return true; + } + + // + // MapScope::scriptStartForced + // + bool MapScope::scriptStartForced(ScriptName name, ScopeID scope, ScriptStartInfo const &info) + { + if(scope != ScopeID{hub->global->id, hub->id, id}) + { + env->deferAction({scope, name, ScriptAction::StartForced, {info.argV, info.argC}}); + return true; + } + + if(Script *script = findScript(name)) + return scriptStartForced(script, info); + else + return false; + } + + // + // MapScope::scriptStartResult + // + Word MapScope::scriptStartResult(Script *script, ScriptStartInfo const &info) + { + Thread *thread = env->getFreeThread(); + + thread->start(script, this, info.info, info.argV, info.argC); + if(info.func) info.func(thread); + if(info.funcc) info.funcc(thread); + thread->exec(); + + Word result = thread->result; + if(thread->state == ThreadState::Inactive) + freeThread(thread); + return result; + } + + // + // MapScope::scriptStartResult + // + Word MapScope::scriptStartResult(ScriptName name, ScriptStartInfo const &info) + { + if(Script *script = findScript(name)) + return scriptStartResult(script, info); + else + return 0; + } + + // + // MapScope::scriptStartType + // + Word MapScope::scriptStartType(Word type, ScriptStartInfo const &info) + { + Word result = 0; + + for(auto &script : pd->scriptThread) + { + if(script.key->type == type) + result += scriptStart(script.key, info); + } + + return result; + } + + // + // MapScope::scriptStartTypeForced + // + Word MapScope::scriptStartTypeForced(Word type, ScriptStartInfo const &info) + { + Word result = 0; + + for(auto &script : pd->scriptThread) + { + if(script.key->type == type) + result += scriptStartForced(script.key, info); + } + + return result; + } + + // + // MapScope::scriptStop + // + bool MapScope::scriptStop(Script *script) + { + auto itr = pd->scriptThread.find(script); + if(!itr || !*itr) + return false; + + switch((*itr)->state.state) + { + case ThreadState::Inactive: + case ThreadState::Stopped: + return false; + + default: + (*itr)->state = ThreadState::Stopped; + (*itr) = nullptr; + return true; + } + } + + // + // MapScope::scriptStop + // + bool MapScope::scriptStop(ScriptName name, ScopeID scope) + { + if(scope != ScopeID{hub->global->id, hub->id, id}) + { + env->deferAction({scope, name, ScriptAction::Stop, {}}); + return true; + } + + if(Script *script = findScript(name)) + return scriptStop(script); + else + return false; + } + + // + // MapScope::unlockStrings + // + void MapScope::unlockStrings() const + { + for(auto &action : scriptAction) + action.unlockStrings(env); + + for(auto &scope : pd->scopes) + scope.val.unlockStrings(); + + for(auto &thread : threadActive) + thread.unlockStrings(); + } + + // + // ModuleScope constructor + // + ModuleScope::ModuleScope(MapScope *map_, Module *module_) : + env {map_->env}, + map {map_}, + module{module_}, + + selfArrV{}, + selfRegV{} + { + // Set arrays and registers to refer to this scope's by default. + for(std::size_t i = 0; i != ArrC; ++i) arrV[i] = &selfArrV[i]; + for(std::size_t i = 0; i != RegC; ++i) regV[i] = &selfRegV[i]; + + // Apply initialization data from module. + + for(std::size_t i = 0; i != ArrC; ++i) + { + if(i < module->arrInitV.size()) + module->arrInitV[i].apply(selfArrV[i], module); + } + + for(std::size_t i = 0; i != RegC; ++i) + { + if(i < module->regInitV.size()) + selfRegV[i] = module->regInitV[i].getValue(module); + } + } + + // + // ModuleScope destructor + // + ModuleScope::~ModuleScope() + { + } + + // + // ModuleScope::import + // + void ModuleScope::import() + { + for(std::size_t i = 0, e = std::min<std::size_t>(ArrC, module->arrImpV.size()); i != e; ++i) + { + String *arrName = module->arrImpV[i]; + if(!arrName) continue; + + for(auto &imp : module->importV) + { + for(auto &impName : imp->arrNameV) + { + if(impName == arrName) + { + std::size_t impIdx = &impName - imp->arrNameV.data(); + if(impIdx >= ArrC) continue; + arrV[i] = &map->getModuleScope(imp)->selfArrV[impIdx]; + goto arr_found; + } + } + } + + arr_found:; + } + + for(std::size_t i = 0, e = std::min<std::size_t>(RegC, module->regImpV.size()); i != e; ++i) + { + String *regName = module->regImpV[i]; + if(!regName) continue; + + for(auto &imp : module->importV) + { + for(auto &impName : imp->regNameV) + { + if(impName == regName) + { + std::size_t impIdx = &impName - imp->regNameV.data(); + if(impIdx >= RegC) continue; + regV[i] = &map->getModuleScope(imp)->selfRegV[impIdx]; + goto reg_found; + } + } + } + + reg_found:; + } + } + + // + // ModuleScope::loadState + // + void ModuleScope::loadState(Serial &in) + { + in.readSign(Signature::ModuleScope); + + for(auto &arr : selfArrV) + arr.loadState(in); + + for(auto ® : selfRegV) + reg = ReadVLN<Word>(in); + + in.readSign(~Signature::ModuleScope); + } + + // + // ModuleScope::lockStrings + // + void ModuleScope::lockStrings() const + { + for(auto &arr : selfArrV) arr.lockStrings(env); + for(auto ® : selfRegV) ++env->getString(reg)->lock; + } + + // + // ModuleScope::refStrings + // + void ModuleScope::refStrings() const + { + for(auto &arr : selfArrV) arr.refStrings(env); + for(auto ® : selfRegV) env->getString(reg)->ref = true; + } + + // + // ModuleScope::saveState + // + void ModuleScope::saveState(Serial &out) const + { + out.writeSign(Signature::ModuleScope); + + for(auto &arr : selfArrV) + arr.saveState(out); + + for(auto ® : selfRegV) + WriteVLN(out, reg); + + out.writeSign(~Signature::ModuleScope); + } + + // + // ModuleScope::unlockStrings + // + void ModuleScope::unlockStrings() const + { + for(auto &arr : selfArrV) arr.unlockStrings(env); + for(auto ® : selfRegV) --env->getString(reg)->lock; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Scope.hpp b/src/acs/vm/ACSVM/Scope.hpp new file mode 100644 index 0000000000000000000000000000000000000000..761e65b2bb5ba76a4171f82fc17b1a5b07c94215 --- /dev/null +++ b/src/acs/vm/ACSVM/Scope.hpp @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Scope classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Scope_H__ +#define ACSVM__Scope_H__ + +#include "Array.hpp" +#include "List.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + extern "C" using MapScope_ScriptStartFuncC = void (*)(void *); + + // + // GlobalScope + // + class GlobalScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + GlobalScope(GlobalScope const &) = delete; + GlobalScope(Environment *env, Word id); + ~GlobalScope(); + + std::size_t countActiveThread() const; + + void exec(); + + void freeHubScope(HubScope *scope); + + HubScope *getHubScope(Word id); + + bool hasActiveThread() const; + + void lockStrings() const; + + void loadState(Serial &in); + + void refStrings() const; + + void reset(); + + void saveState(Serial &out) const; + + void unlockStrings() const; + + Environment *const env; + Word const id; + + Array arrV[ArrC]; + Word regV[RegC]; + + ListLink<GlobalScope> hashLink; + ListLink<ScriptAction> scriptAction; + + bool active; + + private: + struct PrivData; + + PrivData *pd; + }; + + // + // HubScope + // + class HubScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + HubScope(HubScope const &) = delete; + HubScope(GlobalScope *global, Word id); + ~HubScope(); + + std::size_t countActiveThread() const; + + void exec(); + + void freeMapScope(MapScope *scope); + + MapScope *getMapScope(Word id); + + bool hasActiveThread() const; + + void lockStrings() const; + + void loadState(Serial &in); + + void refStrings() const; + + void reset(); + + void saveState(Serial &out) const; + + void unlockStrings() const; + + Environment *const env; + GlobalScope *const global; + Word const id; + + Array arrV[ArrC]; + Word regV[RegC]; + + ListLink<HubScope> hashLink; + ListLink<ScriptAction> scriptAction; + + bool active; + + private: + struct PrivData; + + PrivData *pd; + }; + + // + // MapScope + // + class MapScope + { + public: + using ScriptStartFunc = void (*)(Thread *); + using ScriptStartFuncC = MapScope_ScriptStartFuncC; + + // + // ScriptStartInfo + // + class ScriptStartInfo + { + public: + ScriptStartInfo() : + argV{nullptr}, func{nullptr}, funcc{nullptr}, info{nullptr}, argC{0} {} + ScriptStartInfo(Word const *argV_, std::size_t argC_, + ThreadInfo const *info_ = nullptr, ScriptStartFunc func_ = nullptr) : + argV{argV_}, func{func_}, funcc{nullptr}, info{info_}, argC{argC_} {} + ScriptStartInfo(Word const *argV_, std::size_t argC_, + ThreadInfo const *info_, ScriptStartFuncC func_) : + argV{argV_}, func{nullptr}, funcc{func_}, info{info_}, argC{argC_} {} + + Word const *argV; + ScriptStartFunc func; + ScriptStartFuncC funcc; + ThreadInfo const *info; + std::size_t argC; + }; + + + MapScope(MapScope const &) = delete; + MapScope(HubScope *hub, Word id); + ~MapScope(); + + void addModules(Module *const *moduleV, std::size_t moduleC); + + std::size_t countActiveThread() const; + + void exec(); + + Script *findScript(ScriptName name); + Script *findScript(String *name); + Script *findScript(Word name); + + ModuleScope *getModuleScope(Module *module); + + String *getString(Word idx) const; + + bool hasActiveThread() const; + + bool hasModules() const; + + bool isScriptActive(Script *script); + + void loadState(Serial &in); + + void lockStrings() const; + + void refStrings() const; + + void reset(); + + void saveState(Serial &out) const; + + bool scriptPause(Script *script); + bool scriptPause(ScriptName name, ScopeID scope); + bool scriptStart(Script *script, ScriptStartInfo const &info); + bool scriptStart(ScriptName name, ScopeID scope, ScriptStartInfo const &info); + bool scriptStartForced(Script *script, ScriptStartInfo const &info); + bool scriptStartForced(ScriptName name, ScopeID scope, ScriptStartInfo const &info); + Word scriptStartResult(Script *script, ScriptStartInfo const &info); + Word scriptStartResult(ScriptName name, ScriptStartInfo const &info); + Word scriptStartType(Word type, ScriptStartInfo const &info); + Word scriptStartTypeForced(Word type, ScriptStartInfo const &info); + bool scriptStop(Script *script); + bool scriptStop(ScriptName name, ScopeID scope); + + void unlockStrings() const; + + Environment *const env; + HubScope *const hub; + Word const id; + + ListLink<MapScope> hashLink; + ListLink<ScriptAction> scriptAction; + ListLink<Thread> threadActive; + + // Used for untagged string lookup. + Module *module0; + + bool active; + bool clampCallSpec; + + protected: + void freeThread(Thread *thread); + + private: + struct PrivData; + + void loadModules(Serial &in); + void loadThreads(Serial &in); + + void saveModules(Serial &out) const; + void saveThreads(Serial &out) const; + + PrivData *pd; + }; + + // + // ModuleScope + // + class ModuleScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + ModuleScope(ModuleScope const &) = delete; + ModuleScope(MapScope *map, Module *module); + ~ModuleScope(); + + void import(); + + void loadState(Serial &in); + + void lockStrings() const; + + void refStrings() const; + + void saveState(Serial &out) const; + + void unlockStrings() const; + + Environment *const env; + MapScope *const map; + Module *const module; + + Array *arrV[ArrC]; + Word *regV[RegC]; + + private: + Array selfArrV[ArrC]; + Word selfRegV[RegC]; + }; +} + +#endif//ACSVM__Scope_H__ + diff --git a/src/acs/vm/ACSVM/Script.cpp b/src/acs/vm/ACSVM/Script.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15d2d94a732457b417d5a24b6361f80dcc18d3a9 --- /dev/null +++ b/src/acs/vm/ACSVM/Script.cpp @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Script class. +// +//----------------------------------------------------------------------------- + +#include "Script.hpp" + +#include "Environment.hpp" +#include "Module.hpp" + + +//----------------------------------------------------------------------------| +// Extern Fumnctions | +// + +namespace ACSVM +{ + // + // Script constructor + // + Script::Script(Module *module_) : + module{module_}, + + name{}, + + argC {0}, + codeIdx{0}, + flags {0}, + locArrC{0}, + locRegC{module->env->scriptLocRegC}, + type {0}, + + flagClient{false}, + flagNet {false} + { + } + + // + // Script destructor + // + Script::~Script() + { + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Script.hpp b/src/acs/vm/ACSVM/Script.hpp new file mode 100644 index 0000000000000000000000000000000000000000..33f8f490569ffc841e50af6ae49e5e36fd3fa3f3 --- /dev/null +++ b/src/acs/vm/ACSVM/Script.hpp @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Script class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Script_H__ +#define ACSVM__Script_H__ + +#include "Types.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // ScriptName + // + class ScriptName + { + public: + ScriptName() : s{nullptr}, i{0} {} + ScriptName(String *s_) : s{s_}, i{0} {} + ScriptName(String *s_, Word i_) : s{s_}, i{i_} {} + ScriptName(Word i_) : s{nullptr}, i{i_} {} + + String *s; + Word i; + }; + + // + // Script + // + class Script + { + public: + explicit Script(Module *module); + ~Script(); + + Module *const module; + + ScriptName name; + + Word argC; + Word codeIdx; + Word flags; + Word locArrC; + Word locRegC; + Word type; + + bool flagClient : 1; + bool flagNet : 1; + }; +} + +#endif//ACSVM__Script_H__ + diff --git a/src/acs/vm/ACSVM/Serial.cpp b/src/acs/vm/ACSVM/Serial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83edc0528be18b6f49003032a59ad0304e58533d --- /dev/null +++ b/src/acs/vm/ACSVM/Serial.cpp @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Serialization. +// +//----------------------------------------------------------------------------- + +#include "Serial.hpp" + +#include "BinaryIO.hpp" +#include "Error.hpp" + +#include <cstring> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Serial::loadHead + // + void Serial::loadHead() + { + char buf[6] = {}; + in->read(buf, 6); + + if(std::memcmp(buf, "ACSVM\0", 6)) + throw SerialError{"invalid file signature"}; + + version = ReadVLN<unsigned int>(*in); + + auto flags = ReadVLN<std::uint_fast32_t>(*in); + signs = flags & 0x0001; + } + + // + // Serial::loadTail + // + void Serial::loadTail() + { + readSign(~Signature::Serial); + } + + // + // Serial::readSign + // + void Serial::readSign(Signature sign) + { + if(!signs) return; + + auto got = static_cast<Signature>(ReadLE4(*in)); + + if(sign != got) + throw SerialSignError{sign, got}; + } + + // + // Serial::saveHead + // + void Serial::saveHead() + { + out->write("ACSVM\0", 6); + WriteVLN(*out, 0); + + std::uint_fast32_t flags = 0; + if(signs) flags |= 0x0001; + WriteVLN(*out, flags); + } + + // + // Serial::saveTail + // + void Serial::saveTail() + { + writeSign(~Signature::Serial); + } + + // + // Serial::writeSign + // + void Serial::writeSign(Signature sign) + { + if(!signs) return; + + WriteLE4(*out, static_cast<std::uint32_t>(sign)); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Serial.hpp b/src/acs/vm/ACSVM/Serial.hpp new file mode 100644 index 0000000000000000000000000000000000000000..98e1673724d71d443615510e131f1ffc8e1a0bb5 --- /dev/null +++ b/src/acs/vm/ACSVM/Serial.hpp @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Serialization. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Serial_H__ +#define ACSVM__Serial_H__ + +#include "ID.hpp" + +#include <istream> +#include <ostream> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Signature + // + enum class Signature : std::uint32_t + { + Array = MakeID("ARAY"), + Environment = MakeID("ENVI"), + GlobalScope = MakeID("GBLs"), + HubScope = MakeID("HUBs"), + MapScope = MakeID("MAPs"), + ModuleScope = MakeID("MODs"), + Serial = MakeID("SERI"), + Thread = MakeID("THRD"), + }; + + // + // Serial + // + class Serial + { + public: + Serial(std::istream &in_) : in{&in_} {} + Serial(std::ostream &out_) : out{&out_}, + version{0}, signs{false} {} + + operator std::istream & () {return *in;} + operator std::ostream & () {return *out;} + + void loadHead(); + void loadTail(); + + void readSign(Signature sign); + + void saveHead(); + void saveTail(); + + void writeSign(Signature sign); + + union + { + std::istream *const in; + std::ostream *const out; + }; + + unsigned int version; + bool signs; + }; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + constexpr Signature operator ~ (Signature sign) + {return static_cast<Signature>(~static_cast<std::uint32_t>(sign));} +} + +#endif//ACSVM__Serial_H__ + diff --git a/src/acs/vm/ACSVM/Stack.hpp b/src/acs/vm/ACSVM/Stack.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9fb72cc53e4e033357dab1e9396e5006bef93460 --- /dev/null +++ b/src/acs/vm/ACSVM/Stack.hpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Stack class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Stack_H__ +#define ACSVM__Stack_H__ + +#include <climits> +#include <new> +#include <utility> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Stack + // + // Stack container. + // + template<typename T> + class Stack + { + public: + Stack() : stack{nullptr}, stkEnd{nullptr}, stkPtr{nullptr} {} + ~Stack() {clear(); ::operator delete(stack);} + + // operator [] + T &operator [] (std::size_t idx) {return *(stkPtr - idx);} + + // begin + T *begin() {return stack;} + T const *begin() const {return stack;} + + // clear + void clear() {while(stkPtr != stack) (--stkPtr)->~T();} + + // drop + void drop() {(--stkPtr)->~T();} + void drop(std::size_t n) {while(n--) (--stkPtr)->~T();} + + // empty + bool empty() const {return stkPtr == stack;} + + // end + T *end() {return stkPtr;} + T const *end() const {return stkPtr;} + + // push + void push(T const &value) {new(stkPtr++) T( value );} + void push(T &&value) {new(stkPtr++) T(std::move(value));} + + // + // reserve + // + void reserve(std::size_t count) + { + if(static_cast<std::size_t>(stkEnd - stkPtr) >= count) + return; + + // Save pointers as indexes. + std::size_t idxEnd = stkEnd - stack; + std::size_t idxPtr = stkPtr - stack; + + // Calculate new array size. + if(SIZE_MAX / sizeof(T) - idxEnd < count * 2) + throw std::bad_alloc(); + + idxEnd += count * 2; + + // Allocate and initialize new array. + T *stackNew = static_cast<T *>(::operator new(idxEnd * sizeof(T))); + for(T *itrNew = stackNew, *itr = stack, *end = stkPtr; itr != end;) + { + new(itrNew++) T(std::move(*itr++)); + itr->~T(); + } + + // Free old array. + ::operator delete(stack); + + // Restore pointers. + stack = stackNew; + stkPtr = stack + idxPtr; + stkEnd = stack + idxEnd; + } + + // size + std::size_t size() const {return stkPtr - stack;} + + private: + T *stack; + T *stkEnd; + T *stkPtr; + }; +} + +#endif//ACSVM__Stack_H__ + diff --git a/src/acs/vm/ACSVM/Store.hpp b/src/acs/vm/ACSVM/Store.hpp new file mode 100644 index 0000000000000000000000000000000000000000..713b4a4b8d573a6d8e6587a78fef0a60083f8568 --- /dev/null +++ b/src/acs/vm/ACSVM/Store.hpp @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Store class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Store_H__ +#define ACSVM__Store_H__ + +#include "Types.hpp" + +#include <new> +#include <utility> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Store + // + // Manages storage area for locals. + // + template<typename T> + class Store + { + public: + Store() : store{nullptr}, storeEnd{nullptr}, active{nullptr}, activeEnd{nullptr} {} + ~Store() {clear(); ::operator delete(store);} + + // operator [] + T &operator [] (std::size_t idx) {return active[idx];} + + // + // alloc + // + void alloc(std::size_t count) + { + // Possibly reallocate underlying storage. + if(static_cast<std::size_t>(storeEnd - activeEnd) < count) + { + // Save pointers as indexes. + std::size_t activeIdx = active - store; + std::size_t activeEndIdx = activeEnd - store; + std::size_t storeEndIdx = storeEnd - store; + + // Calculate new array size. + if(SIZE_MAX / sizeof(T) - storeEndIdx < count * 2) + throw std::bad_alloc(); + + storeEndIdx += count * 2; + + // Allocate and initialize new array. + T *storeNew = static_cast<T *>(::operator new(storeEndIdx * sizeof(T))); + for(T *out = storeNew, *in = store, *end = activeEnd; in != end; ++out, ++in) + { + new(out) T(std::move(*in)); + in->~T(); + } + + // Free old array. + ::operator delete(store); + + // Restore pointers. + store = storeNew; + active = store + activeIdx; + activeEnd = store + activeEndIdx; + storeEnd = store + storeEndIdx; + } + + active = activeEnd; + while(count--) new(activeEnd++) T{}; + } + + // + // allocLoad + // + // Allocates storage for loading from saved state. countFull elements are + // value-initialized and count elements are made available. That is, they + // should correspond to a prior call to sizeFull and size, respectively. + // + void allocLoad(std::size_t countFull, std::size_t count) + { + clear(); + alloc(countFull); + active = activeEnd - count; + } + + // begin + T *begin() {return active;} + T const *begin() const {return active;} + + // beginFull + T *beginFull() {return store;} + T const *beginFull() const {return store;} + + // + // clear + // + void clear() + { + while(activeEnd != store) + (--activeEnd)->~T(); + + active = store; + } + + // dataFull + T const *dataFull() const {return store;} + + // end + T *end() {return activeEnd;} + T const *end() const {return activeEnd;} + + // + // free + // + // count must be the size (in elements) of the previous allocation. + // + void free(std::size_t count) + { + while(activeEnd != active) + (--activeEnd)->~T(); + + active -= count; + } + + // size + std::size_t size() const {return activeEnd - active;} + + // sizeFull + std::size_t sizeFull() const {return activeEnd - store;} + + private: + T *store, *storeEnd; + T *active, *activeEnd; + }; +} + +#endif//ACSVM__Store_H__ + diff --git a/src/acs/vm/ACSVM/String.cpp b/src/acs/vm/ACSVM/String.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0b0b59524d82ce6cef0a09556bff67f90e0e8c1 --- /dev/null +++ b/src/acs/vm/ACSVM/String.cpp @@ -0,0 +1,354 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// String classes. +// +//----------------------------------------------------------------------------- + +#include "String.hpp" + +#include "BinaryIO.hpp" +#include "HashMap.hpp" + +#include <new> +#include <vector> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // StringTable::PrivData + // + struct StringTable::PrivData + { + std::vector<Word> freeIdx; + + HashMapKeyObj<StringData, String, &String::link> stringByData{64, 64}; + std::vector<String *> stringByIdx; + }; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // String constructor + // + String::String(StringData const &data, Word idx_) : + StringData{data}, lock{0}, idx{idx_}, len0(std::strlen(str)), link{this} + { + } + + // + // String destructor + // + String::~String() + { + } + + // + // String::Delete + // + void String::Delete(String *str) + { + str->~String(); + operator delete(str); + } + + // + // String::New + // + String *String::New(StringData const &data, Word idx) + { + String *str = static_cast<String *>(operator new(sizeof(String) + data.len + 1)); + char *buf = reinterpret_cast<char *>(str + 1); + + memcpy(buf, data.str, data.len); + buf[data.len] = '\0'; + + return new(str) String{{buf, data.len, data.hash}, idx}; + } + + // + // String::Read + // + String *String::Read(std::istream &in, Word idx) + { + std::size_t len = ReadVLN<std::size_t>(in); + + String *str = static_cast<String *>(operator new(sizeof(String) + len + 1)); + char *buf = reinterpret_cast<char *>(str + 1); + + in.read(buf, len); + buf[len] = '\0'; + + return new(str) String{{buf, len, StrHash(buf, len)}, idx}; + } + + // + // String::Write + // + void String::Write(std::ostream &out, String *in) + { + WriteVLN(out, in->len); + out.write(in->str, in->len); + } + + // + // StringTable constructor + // + StringTable::StringTable() : + strV{nullptr}, + strC{0}, + + strNone{String::New({"", 0, 0}, 0)}, + + pd{new PrivData} + { + } + + // + // StringTable move constructor + // + StringTable::StringTable(StringTable &&table) : + strV{table.strV}, + strC{table.strC}, + + strNone{table.strNone}, + + pd{table.pd} + { + table.strV = nullptr; + table.strC = 0; + + table.strNone = nullptr; + + table.pd = nullptr; + } + + // + // StringTable destructor + // + StringTable::~StringTable() + { + if(!pd) return; + + clear(); + + delete pd; + + String::Delete(strNone); + } + + // + // StringTable::operator [StringData] + // + String &StringTable::operator [] (StringData const &data) + { + if(auto str = pd->stringByData.find(data)) return *str; + + Word idx; + if(pd->freeIdx.empty()) + { + // Index has to fit within Word size. + // If size_t has an equal or lesser max, then the check is redundant, + // and some compilers warn about that kind of tautological comparison. + #if SIZE_MAX > UINT32_MAX + if(pd->stringByIdx.size() > UINT32_MAX) + throw std::bad_alloc(); + #endif + + idx = pd->stringByIdx.size(); + pd->stringByIdx.emplace_back(strNone); + strV = pd->stringByIdx.data(); + strC = pd->stringByIdx.size(); + } + else + { + idx = pd->freeIdx.back(); + pd->freeIdx.pop_back(); + } + + String *str = String::New(data, idx); + pd->stringByIdx[idx] = str; + pd->stringByData.insert(str); + return *str; + } + + // + // StringTable::clear + // + void StringTable::clear() + { + for(auto &str : pd->stringByIdx) + { + if(str != strNone) + String::Delete(str); + } + + pd->freeIdx.clear(); + pd->stringByData.clear(); + pd->stringByIdx.clear(); + + strV = nullptr; + strC = 0; + } + + // + // StringTable::collectBegin + // + void StringTable::collectBegin() + { + for(auto &str : pd->stringByData) + str.ref = false; + } + + // + // StringTable.collectEnd + // + void StringTable::collectEnd() + { + for(auto itr = pd->stringByData.begin(), end = pd->stringByData.end(); itr != end;) + { + if(!itr->ref && !itr->lock) + { + String &str = *itr++; + pd->stringByIdx[str.idx] = strNone; + pd->freeIdx.push_back(str.idx); + pd->stringByData.unlink(&str); + String::Delete(&str); + } + else + ++itr; + } + } + + // + // StringTable::loadState + // + void StringTable::loadState(std::istream &in) + { + if(pd) + { + clear(); + } + else + { + pd = new PrivData; + strNone = String::New({"", 0, 0}, 0); + } + + auto count = ReadVLN<std::size_t>(in); + + pd->stringByIdx.resize(count); + strV = pd->stringByIdx.data(); + strC = pd->stringByIdx.size(); + + for(std::size_t idx = 0; idx != count; ++idx) + { + if(in.get()) + { + String *str = String::Read(in, idx); + str->lock = ReadVLN<std::size_t>(in); + pd->stringByIdx[idx] = str; + pd->stringByData.insert(str); + } + else + { + pd->stringByIdx[idx] = strNone; + pd->freeIdx.emplace_back(idx); + } + } + } + + // + // StringTable::saveState + // + void StringTable::saveState(std::ostream &out) const + { + WriteVLN(out, pd->stringByIdx.size()); + + for(String *&str : pd->stringByIdx) + { + if(str != strNone) + { + out << '\1'; + + String::Write(out, str); + WriteVLN(out, str->lock); + } + else + out << '\0'; + } + } + + // + // StringTable::size + // + std::size_t StringTable::size() const + { + return pd->stringByData.size(); + } + + // + // StrDup + // + std::unique_ptr<char[]> StrDup(char const *str) + { + return StrDup(str, std::strlen(str)); + } + + // + // StrDup + // + std::unique_ptr<char[]> StrDup(char const *str, std::size_t len) + { + std::unique_ptr<char[]> dup{new char[len + 1]}; + std::memcpy(dup.get(), str, len); + dup[len] = '\0'; + + return dup; + } + + // + // StrHash + // + std::size_t StrHash(char const *str) + { + std::size_t hash = 0; + + if(str) while(*str) + hash = hash * 5 + static_cast<unsigned char>(*str++); + + return hash; + } + + // + // StrHash + // + std::size_t StrHash(char const *str, std::size_t len) + { + std::size_t hash = 0; + + while(len--) + hash = hash * 5 + static_cast<unsigned char>(*str++); + + return hash; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/String.hpp b/src/acs/vm/ACSVM/String.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7c46e4f9205bb4572c71e30cedeff6d8d962e0c2 --- /dev/null +++ b/src/acs/vm/ACSVM/String.hpp @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// String classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__String_H__ +#define ACSVM__String_H__ + +#include "List.hpp" +#include "Types.hpp" + +#include <cstring> +#include <functional> +#include <memory> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + std::size_t StrHash(char const *str, std::size_t len); + + // + // StringData + // + // Stores basic string information. Does not manage the storage for the + // string data. + // + class StringData + { + public: + StringData(char const *first, char const *last) : + str{first}, len(last - first), hash{StrHash(str, len)} {} + StringData(char const *str_, std::size_t len_) : + str{str_}, len{len_}, hash{StrHash(str, len)} {} + StringData(char const *str_, std::size_t len_, std::size_t hash_) : + str{str_}, len{len_}, hash{hash_} {} + + bool operator == (StringData const &r) const + {return hash == r.hash && len == r.len && !std::memcmp(str, r.str, len);} + + char const *const str; + std::size_t const len; + std::size_t const hash; + }; + + // + // String + // + // Indexed string data. + // + class String : public StringData + { + public: + std::size_t lock; + + Word const idx; // Index into table. + Word const len0; // Null-terminated length. + + bool ref; + + char get(std::size_t i) const {return i < len ? str[i] : '\0';} + + + friend class StringTable; + + private: + String(StringData const &data, Word idx); + ~String(); + + ListLink<String> link; + + + static void Delete(String *str); + + static String *New(StringData const &data, Word idx); + + static String *Read(std::istream &in, Word idx); + + static void Write(std::ostream &out, String *in); + }; + + // + // StringTable + // + class StringTable + { + public: + StringTable(); + StringTable(StringTable &&table); + ~StringTable(); + + String &operator [] (Word idx) const + {return idx < strC ? *strV[idx] : *strNone;} + String &operator [] (StringData const &data); + + void clear(); + + void collectBegin(); + void collectEnd(); + + String &getNone() {return *strNone;} + + void loadState(std::istream &in); + + void saveState(std::ostream &out) const; + + std::size_t size() const; + + private: + struct PrivData; + + String **strV; + std::size_t strC; + + String *strNone; + + PrivData *pd; + }; +} + +namespace std +{ + // + // hash<::ACSVM::StringData> + // + template<> + struct hash<::ACSVM::StringData> + { + size_t operator () (::ACSVM::StringData const &data) const + {return data.hash;} + }; +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + std::unique_ptr<char[]> StrDup(char const *str); + std::unique_ptr<char[]> StrDup(char const *str, std::size_t len); + + std::size_t StrHash(char const *str); + std::size_t StrHash(char const *str, std::size_t len); +} + +#endif//ACSVM__String_H__ + diff --git a/src/acs/vm/ACSVM/Thread.cpp b/src/acs/vm/ACSVM/Thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4d19a7a8c30a070656f35a3732a6804ba627340 --- /dev/null +++ b/src/acs/vm/ACSVM/Thread.cpp @@ -0,0 +1,292 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Thread classes. +// +//----------------------------------------------------------------------------- + +#include "Thread.hpp" + +#include "Array.hpp" +#include "BinaryIO.hpp" +#include "Environment.hpp" +#include "Module.hpp" +#include "Scope.hpp" +#include "Script.hpp" +#include "Serial.hpp" + +#include <algorithm> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Thread constructor + // + Thread::Thread(Environment *env_) : + env{env_}, + + link{this}, + + codePtr {nullptr}, + module {nullptr}, + scopeGbl{nullptr}, + scopeHub{nullptr}, + scopeMap{nullptr}, + scopeMod{nullptr}, + script {nullptr}, + delay {0}, + result {0} + { + } + + // + // Thread destructor + // + Thread::~Thread() + { + } + + // + // Thread::getInfo + // + ThreadInfo const *Thread::getInfo() const + { + return nullptr; + } + + // + // Thread::loadState + // + void Thread::loadState(Serial &in) + { + std::size_t count, countFull; + + in.readSign(Signature::Thread); + + module = env->getModule(env->readModuleName(in)); + codePtr = &module->codeV[ReadVLN<std::size_t>(in)]; + scopeGbl = env->getGlobalScope(ReadVLN<Word>(in)); + scopeHub = scopeGbl->getHubScope(ReadVLN<Word>(in)); + scopeMap = scopeHub->getMapScope(ReadVLN<Word>(in)); + scopeMod = scopeMap->getModuleScope(module); + script = env->readScript(in); + delay = ReadVLN<Word>(in); + result = ReadVLN<Word>(in); + + count = ReadVLN<std::size_t>(in); + callStk.clear(); callStk.reserve(count + CallStkSize); + while(count--) + callStk.push(readCallFrame(in)); + + count = ReadVLN<std::size_t>(in); + dataStk.clear(); dataStk.reserve(count + DataStkSize); + while(count--) + dataStk.push(ReadVLN<Word>(in)); + + countFull = ReadVLN<std::size_t>(in); + count = ReadVLN<std::size_t>(in); + localArr.allocLoad(countFull, count); + for(auto itr = localArr.beginFull(), end = localArr.end(); itr != end; ++itr) + itr->loadState(in); + + countFull = ReadVLN<std::size_t>(in); + count = ReadVLN<std::size_t>(in); + localReg.allocLoad(countFull, count); + for(auto itr = localReg.beginFull(), end = localReg.end(); itr != end; ++itr) + *itr = ReadVLN<Word>(in); + + countFull = ReadVLN<std::size_t>(in); + count = ReadVLN<std::size_t>(in); + in.in->read(printBuf.getLoadBuf(countFull, count), countFull); + + state.state = static_cast<ThreadState::State>(ReadVLN<int>(in)); + state.data = ReadVLN<Word>(in); + state.type = ReadVLN<Word>(in); + + in.readSign(~Signature::Thread); + } + + // + // Thread::lockStrings + // + void Thread::lockStrings() const + { + for(auto &data : dataStk) + ++env->getString(data)->lock; + + for(auto arr = localArr.beginFull(), end = localArr.end(); arr != end; ++arr) + arr->lockStrings(env); + + for(auto reg = localReg.beginFull(), end = localReg.end(); reg != end; ++reg) + ++env->getString(*reg)->lock; + + if(state == ThreadState::WaitScrS) + ++env->getString(state.data)->lock; + } + + // + // Thread::readCallFrame + // + CallFrame Thread::readCallFrame(Serial &in) const + { + CallFrame out; + + out.module = env->getModule(env->readModuleName(in)); + out.scopeMod = scopeMap->getModuleScope(out.module); + out.codePtr = &out.module->codeV[ReadVLN<std::size_t>(in)]; + out.locArrC = ReadVLN<std::size_t>(in); + out.locRegC = ReadVLN<std::size_t>(in); + + return out; + } + + // + // Thread::refStrings + // + void Thread::refStrings() const + { + for(auto &data : dataStk) + env->getString(data)->ref = true; + + for(auto arr = localArr.beginFull(), end = localArr.end(); arr != end; ++arr) + arr->refStrings(env); + + for(auto reg = localReg.beginFull(), end = localReg.end(); reg != end; ++reg) + env->getString(*reg)->ref = true; + + if(state == ThreadState::WaitScrS) + env->getString(state.data)->ref = true; + } + + // + // Thread::saveState + // + void Thread::saveState(Serial &out) const + { + out.writeSign(Signature::Thread); + + env->writeModuleName(out, module->name); + WriteVLN(out, codePtr - module->codeV.data()); + WriteVLN(out, scopeGbl->id); + WriteVLN(out, scopeHub->id); + WriteVLN(out, scopeMap->id); + env->writeScript(out, script); + WriteVLN(out, delay); + WriteVLN(out, result); + + WriteVLN(out, callStk.size()); + for(auto &call : callStk) + writeCallFrame(out, call); + + WriteVLN(out, dataStk.size()); + for(auto &data : dataStk) + WriteVLN(out, data); + + WriteVLN(out, localArr.sizeFull()); + WriteVLN(out, localArr.size()); + for(auto itr = localArr.beginFull(), end = localArr.end(); itr != end; ++itr) + itr->saveState(out); + + WriteVLN(out, localReg.sizeFull()); + WriteVLN(out, localReg.size()); + for(auto itr = localReg.beginFull(), end = localReg.end(); itr != end; ++itr) + WriteVLN(out, *itr); + + WriteVLN(out, printBuf.sizeFull()); + WriteVLN(out, printBuf.size()); + out.out->write(printBuf.dataFull(), printBuf.sizeFull()); + + WriteVLN<int>(out, state.state); + WriteVLN(out, state.data); + WriteVLN(out, state.type); + + out.writeSign(~Signature::Thread); + } + + // + // Thread::start + // + void Thread::start(Script *script_, MapScope *map, ThreadInfo const *, + Word const *argV, Word argC) + { + link.insert(&map->threadActive); + + script = script_; + module = script->module; + codePtr = &module->codeV[script->codeIdx]; + + scopeMod = map->getModuleScope(module); + scopeMap = map; + scopeHub = scopeMap->hub; + scopeGbl = scopeHub->global; + + callStk.reserve(CallStkSize); + dataStk.reserve(DataStkSize); + localArr.alloc(script->locArrC); + localReg.alloc(script->locRegC); + + std::copy(argV, argV + std::min<Word>(argC, script->argC), &localReg[0]); + + delay = 0; + result = 0; + state = ThreadState::Running; + } + + // + // Thread::stop + // + void Thread::stop() + { + // Release execution resources. + callStk.clear(); + dataStk.clear(); + localArr.clear(); + localReg.clear(); + printBuf.clear(); + + // Set state. + state = ThreadState::Inactive; + } + + // + // Thread::unlockStrings + // + void Thread::unlockStrings() const + { + for(auto &data : dataStk) + --env->getString(data)->lock; + + for(auto arr = localArr.beginFull(), end = localArr.end(); arr != end; ++arr) + arr->unlockStrings(env); + + for(auto reg = localReg.beginFull(), end = localReg.end(); reg != end; ++reg) + --env->getString(*reg)->lock; + + if(state == ThreadState::WaitScrS) + --env->getString(state.data)->lock; + } + + // + // Thread::writeCallFrame + // + void Thread::writeCallFrame(Serial &out, CallFrame const &in) const + { + env->writeModuleName(out, in.module->name); + WriteVLN(out, in.codePtr - in.module->codeV.data()); + WriteVLN(out, in.locArrC); + WriteVLN(out, in.locRegC); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Thread.hpp b/src/acs/vm/ACSVM/Thread.hpp new file mode 100644 index 0000000000000000000000000000000000000000..06d4b34d0d11173888ad51eb21f668862ff22c4b --- /dev/null +++ b/src/acs/vm/ACSVM/Thread.hpp @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Thread classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Thread_H__ +#define ACSVM__Thread_H__ + +#include "List.hpp" +#include "PrintBuf.hpp" +#include "Stack.hpp" +#include "Store.hpp" + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // CallFrame + // + // Stores a call frame for execution. + // + class CallFrame + { + public: + Word const *codePtr; + Module *module; + ModuleScope *scopeMod; + std::size_t locArrC; + std::size_t locRegC; + }; + + // + // ThreadState + // + class ThreadState + { + public: + enum State + { + Inactive, // Inactive thread. + Running, // Running. + Stopped, // Will go inactive on next exec. + Paused, // Paused by instruction. + WaitScrI, // Waiting on a numbered script. + WaitScrS, // Waiting on a named script. + WaitTag, // Waiting on tagged object. + }; + + + ThreadState() : state{Inactive}, data{0}, type{0} {} + ThreadState(State state_) : + state{state_}, data{0}, type{0} {} + ThreadState(State state_, Word data_) : + state{state_}, data{data_}, type{0} {} + ThreadState(State state_, Word data_, Word type_) : + state{state_}, data{data_}, type{type_} {} + + bool operator == (State s) const {return state == s;} + bool operator != (State s) const {return state != s;} + + State state; + + // Extra state data. Used by: + // WaitScrI - Script number. + // WaitScrS - Script name index. + // WaitTag - Tag number. + Word data; + + // Extra state data. Used by: + // WaitTag - Tag type. + Word type; + }; + + // + // ThreadInfo + // + // Derived classes can be used to pass extra information to started threads. + // + class ThreadInfo + { + public: + virtual ~ThreadInfo() {} + }; + + // + // Thread + // + class Thread + { + public: + Thread(Environment *env); + virtual ~Thread(); + + void exec(); + + virtual ThreadInfo const *getInfo() const; + + virtual void loadState(Serial &in); + + virtual void lockStrings() const; + + virtual void refStrings() const; + + virtual void saveState(Serial &out) const; + + virtual void start(Script *script, MapScope *map, ThreadInfo const *info, + Word const *argV, Word argC); + + virtual void stop(); + + virtual void unlockStrings() const; + + Environment *const env; + + ListLink<Thread> link; + + Stack<CallFrame> callStk; + Stack<Word> dataStk; + Store<Array> localArr; + Store<Word> localReg; + PrintBuf printBuf; + ThreadState state; + + Word const *codePtr; // Instruction pointer. + Module *module; // Current execution Module. + GlobalScope *scopeGbl; + HubScope *scopeHub; + MapScope *scopeMap; + ModuleScope *scopeMod; + Script *script; // Current execution Script. + Word delay; // Execution delay tics. + Word result; // Code-defined thread result. + + + static constexpr std::size_t CallStkSize = 8; + static constexpr std::size_t DataStkSize = 256; + + private: + CallFrame readCallFrame(Serial &in) const; + + void writeCallFrame(Serial &out, CallFrame const &in) const; + }; +} + +#endif//ACSVM__Thread_H__ + diff --git a/src/acs/vm/ACSVM/ThreadExec.cpp b/src/acs/vm/ACSVM/ThreadExec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6754800a4e3cd5594088c3a709932cd52db07e45 --- /dev/null +++ b/src/acs/vm/ACSVM/ThreadExec.cpp @@ -0,0 +1,643 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Thread execution. +// +//----------------------------------------------------------------------------- + +#include "Thread.hpp" + +#include "Array.hpp" +#include "Code.hpp" +#include "Environment.hpp" +#include "Function.hpp" +#include "Jump.hpp" +#include "Module.hpp" +#include "Scope.hpp" +#include "Script.hpp" + + +//----------------------------------------------------------------------------| +// Macros | +// + +// +// ACSVM_DynamicGoto +// +// If nonzero, enables use of dynamic goto labels in core interpreter loop. +// Currently, only gcc syntax is supported. +// +#ifndef ACSVM_DynamicGoto +#if defined(__GNUC__) +#define ACSVM_DynamicGoto 1 +#else +#define ACSVM_DynamicGoto 0 +#endif +#endif + +// +// BranchTo +// +#define BranchTo(target) \ + do \ + { \ + codePtr = &module->codeV[(target)]; \ + CountBranch(); \ + } \ + while(0) + +// +// CountBranch +// +// Used to limit the number of branches to prevent infinite no-delay loops. +// +#define CountBranch() \ + if(branches && !--branches) \ + { \ + env->printKill(this, static_cast<Word>(KillType::BranchLimit), 0); \ + goto thread_stop; \ + } \ + else \ + ((void)0) + +// +// DeclCase +// +#if ACSVM_DynamicGoto +#define DeclCase(name) case_Code##name +#else +#define DeclCase(name) case static_cast<Word>(Code::name) +#endif + +// +// NextCase +// +#if ACSVM_DynamicGoto +#define NextCase() goto *cases[*codePtr++] +#else +#define NextCase() goto next_case +#endif + +// +// Op_* +// + +#define Op_AddU(lop) (dataStk.drop(), (lop) += dataStk[0]) +#define Op_AndU(lop) (dataStk.drop(), (lop) &= dataStk[0]) +#define Op_CmpI_GE(lop) (dataStk.drop(), OpFunc_CmpI_GE(lop, dataStk[0])) +#define Op_CmpI_GT(lop) (dataStk.drop(), OpFunc_CmpI_GT(lop, dataStk[0])) +#define Op_CmpI_LE(lop) (dataStk.drop(), OpFunc_CmpI_LE(lop, dataStk[0])) +#define Op_CmpI_LT(lop) (dataStk.drop(), OpFunc_CmpI_LT(lop, dataStk[0])) +#define Op_CmpU_EQ(lop) (dataStk.drop(), OpFunc_CmpU_EQ(lop, dataStk[0])) +#define Op_CmpU_NE(lop) (dataStk.drop(), OpFunc_CmpU_NE(lop, dataStk[0])) +#define Op_DecU(lop) (--(lop)) +#define Op_DivI(lop) (dataStk.drop(), OpFunc_DivI(lop, dataStk[0])) +#define Op_DivX(lop) (dataStk.drop(), OpFunc_DivX(lop, dataStk[0])) +#define Op_Drop(lop) (dataStk.drop(), (lop) = dataStk[0]) +#define Op_IncU(lop) (++(lop)) +#define Op_LAnd(lop) (dataStk.drop(), OpFunc_LAnd(lop, dataStk[0])) +#define Op_LOrI(lop) (dataStk.drop(), OpFunc_LOrI(lop, dataStk[0])) +#define Op_ModI(lop) (dataStk.drop(), OpFunc_ModI(lop, dataStk[0])) +#define Op_MulU(lop) (dataStk.drop(), (lop) *= dataStk[0]) +#define Op_MulX(lop) (dataStk.drop(), OpFunc_MulX(lop, dataStk[0])) +#define Op_OrIU(lop) (dataStk.drop(), (lop) |= dataStk[0]) +#define Op_OrXU(lop) (dataStk.drop(), (lop) ^= dataStk[0]) +#define Op_ShLU(lop) (dataStk.drop(), (lop) <<= dataStk[0] & 31) +#define Op_ShRI(lop) (dataStk.drop(), OpFunc_ShRI(lop, dataStk[0])) +#define Op_SubU(lop) (dataStk.drop(), (lop) -= dataStk[0]) + +// +// OpSet +// +#define OpSet(op) \ + DeclCase(op##_GblArr): \ + Op_##op(scopeGbl->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \ + NextCase(); \ + DeclCase(op##_GblReg): \ + Op_##op(scopeGbl->regV[*codePtr++]); \ + NextCase(); \ + DeclCase(op##_HubArr): \ + Op_##op(scopeHub->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \ + NextCase(); \ + DeclCase(op##_HubReg): \ + Op_##op(scopeHub->regV[*codePtr++]); \ + NextCase(); \ + DeclCase(op##_LocArr): \ + Op_##op(localArr[*codePtr++][dataStk[1]]); dataStk.drop(); \ + NextCase(); \ + DeclCase(op##_LocReg): \ + Op_##op(localReg[*codePtr++]); \ + NextCase(); \ + DeclCase(op##_ModArr): \ + Op_##op((*scopeMod->arrV[*codePtr++])[dataStk[1]]); dataStk.drop(); \ + NextCase(); \ + DeclCase(op##_ModReg): \ + Op_##op(*scopeMod->regV[*codePtr++]); \ + NextCase() + + +//----------------------------------------------------------------------------| +// Static Functions | +// + +namespace ACSVM +{ + // + // OpFunc_CmpI_GE + // + static inline void OpFunc_CmpI_GE(Word &lop, Word rop) + { + lop = static_cast<SWord>(lop) >= static_cast<SWord>(rop); + } + + // + // OpFunc_CmpI_GT + // + static inline void OpFunc_CmpI_GT(Word &lop, Word rop) + { + lop = static_cast<SWord>(lop) > static_cast<SWord>(rop); + } + + // + // OpFunc_CmpI_LE + // + static inline void OpFunc_CmpI_LE(Word &lop, Word rop) + { + lop = static_cast<SWord>(lop) <= static_cast<SWord>(rop); + } + + // + // OpFunc_CmpI_LT + // + static inline void OpFunc_CmpI_LT(Word &lop, Word rop) + { + lop = static_cast<SWord>(lop) < static_cast<SWord>(rop); + } + + // + // OpFunc_CmpU_EQ + // + static inline void OpFunc_CmpU_EQ(Word &lop, Word rop) + { + lop = lop == rop; + } + + // + // OpFunc_CmpU_NE + // + static inline void OpFunc_CmpU_NE(Word &lop, Word rop) + { + lop = lop != rop; + } + + // + // OpFunc_DivI + // + static inline void OpFunc_DivI(Word &lop, Word rop) + { + lop = rop ? static_cast<SWord>(lop) / static_cast<SWord>(rop) : 0; + } + + // + // OpFunc_DivX + // + static inline void OpFunc_DivX(Word &lop, Word rop) + { + if(rop) + lop = (SDWord(SWord(lop)) << 16) / SWord(rop); + else + lop = 0; + } + + // + // OpFunc_LAnd + // + static inline void OpFunc_LAnd(Word &lop, Word rop) + { + lop = lop && rop; + } + + // + // OpFunc_LOrI + // + static inline void OpFunc_LOrI(Word &lop, Word rop) + { + lop = lop || rop; + } + + // + // OpFunc_ModI + // + static inline void OpFunc_ModI(Word &lop, Word rop) + { + lop = rop ? static_cast<SWord>(lop) % static_cast<SWord>(rop) : 0; + } + + // + // OpFunc_MulX + // + static inline void OpFunc_MulX(Word &lop, Word rop) + { + lop = DWord(SDWord(SWord(lop)) * SWord(rop)) >> 16; + } + + // + // OpFunc_ShRI + // + static inline void OpFunc_ShRI(Word &lop, Word rop) + { + // TODO: Implement this without relying on sign-extending shift. + lop = static_cast<SWord>(lop) >> (rop & 31); + } +} + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // Thread::exec + // + void Thread::exec() + { + if(delay && --delay) + return; + + auto branches = env->branchLimit; + + exec_intr: + switch(state.state) + { + case ThreadState::Inactive: return; + case ThreadState::Stopped: goto thread_stop; + case ThreadState::Paused: return; + + case ThreadState::Running: + if(delay) + return; + break; + + case ThreadState::WaitScrI: + if(scopeMap->isScriptActive(scopeMap->findScript(state.data))) + return; + state = ThreadState::Running; + break; + + case ThreadState::WaitScrS: + if(scopeMap->isScriptActive(scopeMap->findScript(scopeMap->getString(state.data)))) + return; + state = ThreadState::Running; + break; + + case ThreadState::WaitTag: + if(!module->env->checkTag(state.type, state.data)) + return; + state = ThreadState::Running; + break; + } + + #if ACSVM_DynamicGoto + static void const *const cases[] = + { + #define ACSVM_CodeList(name, ...) &&case_Code##name, + #include "CodeList.hpp" + }; + #endif + + #if ACSVM_DynamicGoto + NextCase(); + #else + next_case: switch(*codePtr++) + #endif + { + DeclCase(Nop): + NextCase(); + + DeclCase(Kill): + module->env->printKill(this, codePtr[0], codePtr[1]); + goto thread_stop; + + //================================================ + // Binary operator codes. + // + + OpSet(AddU); + OpSet(AndU); + OpSet(DivI); + OpSet(ModI); + OpSet(MulU); + OpSet(OrIU); + OpSet(OrXU); + OpSet(ShLU); + OpSet(ShRI); + OpSet(SubU); + + DeclCase(AddU): Op_AddU(dataStk[1]); NextCase(); + DeclCase(AndU): Op_AndU(dataStk[1]); NextCase(); + DeclCase(CmpI_GE): Op_CmpI_GE(dataStk[1]); NextCase(); + DeclCase(CmpI_GT): Op_CmpI_GT(dataStk[1]); NextCase(); + DeclCase(CmpI_LE): Op_CmpI_LE(dataStk[1]); NextCase(); + DeclCase(CmpI_LT): Op_CmpI_LT(dataStk[1]); NextCase(); + DeclCase(CmpU_EQ): Op_CmpU_EQ(dataStk[1]); NextCase(); + DeclCase(CmpU_NE): Op_CmpU_NE(dataStk[1]); NextCase(); + DeclCase(DivI): Op_DivI(dataStk[1]); NextCase(); + DeclCase(DivX): Op_DivX(dataStk[1]); NextCase(); + DeclCase(LAnd): Op_LAnd(dataStk[1]); NextCase(); + DeclCase(LOrI): Op_LOrI(dataStk[1]); NextCase(); + DeclCase(ModI): Op_ModI(dataStk[1]); NextCase(); + DeclCase(MulU): Op_MulU(dataStk[1]); NextCase(); + DeclCase(MulX): Op_MulX(dataStk[1]); NextCase(); + DeclCase(OrIU): Op_OrIU(dataStk[1]); NextCase(); + DeclCase(OrXU): Op_OrXU(dataStk[1]); NextCase(); + DeclCase(ShLU): Op_ShLU(dataStk[1]); NextCase(); + DeclCase(ShRI): Op_ShRI(dataStk[1]); NextCase(); + DeclCase(SubU): Op_SubU(dataStk[1]); NextCase(); + + //================================================ + // Call codes. + // + + DeclCase(Call_Lit): + { + Function *func; + + func = *codePtr < module->functionV.size() ? module->functionV[*codePtr] : nullptr; + ++codePtr; + + do_call: + if(!func) {BranchTo(0); NextCase();} + + // Reserve stack space. + callStk.reserve(CallStkSize); + dataStk.reserve(DataStkSize); + + // Push call frame. + callStk.push({codePtr, module, scopeMod, localArr.size(), localReg.size()}); + + // Apply function data. + codePtr = &func->module->codeV[func->codeIdx]; + module = func->module; + scopeMod = scopeMap->getModuleScope(module); + localArr.alloc(func->locArrC); + localReg.alloc(func->locRegC); + + // Read arguments. + dataStk.drop(func->argC); + memcpy(&localReg[0], &dataStk[0], func->argC * sizeof(Word)); + + NextCase(); + + DeclCase(Call_Stk): + dataStk.drop(); + func = env->getFunction(dataStk[0]); + goto do_call; + } + + DeclCase(CallFunc): + { + Word argc = *codePtr++; + Word func = *codePtr++; + dataStk.drop(argc); + if(env->callFunc(this, func, &dataStk[0], argc)) + goto exec_intr; + } + NextCase(); + + DeclCase(CallFunc_Lit): + { + Word argc = *codePtr++; + Word func = *codePtr++; + Word const *argv = codePtr; + codePtr += argc; + if(env->callFunc(this, func, argv, argc)) + goto exec_intr; + } + NextCase(); + + DeclCase(CallSpec): + { + Word argc = *codePtr++; + Word spec = *codePtr++; + dataStk.drop(argc); + env->callSpec(this, spec, &dataStk[0], argc); + } + NextCase(); + + DeclCase(CallSpec_Lit): + { + Word argc = *codePtr++; + Word spec = *codePtr++; + Word const *argv = codePtr; + codePtr += argc; + env->callSpec(this, spec, argv, argc); + } + NextCase(); + + DeclCase(CallSpec_R1): + { + Word argc = *codePtr++; + Word spec = *codePtr++; + dataStk.drop(argc); + dataStk.push(env->callSpec(this, spec, &dataStk[0], argc)); + } + NextCase(); + + DeclCase(Retn): + // If no call frames left, terminate the thread. + if(callStk.empty()) + goto thread_stop; + + // Apply call frame. + codePtr = callStk[1].codePtr; + module = callStk[1].module; + scopeMod = callStk[1].scopeMod; + localArr.free(callStk[1].locArrC); + localReg.free(callStk[1].locRegC); + + // Drop call frame. + callStk.drop(); + + NextCase(); + + //================================================ + // Drop codes. + // + + OpSet(Drop); + + DeclCase(Drop_Nul): + dataStk.drop(); + NextCase(); + + DeclCase(Drop_ScrRet): + dataStk.drop(); + result = dataStk[0]; + NextCase(); + + //================================================ + // Jump codes. + // + + DeclCase(Jcnd_Lit): + if(dataStk[1] == *codePtr++) + { + dataStk.drop(); + BranchTo(*codePtr); + } + else + ++codePtr; + NextCase(); + + DeclCase(Jcnd_Nil): + if(dataStk.drop(), dataStk[0]) + ++codePtr; + else + BranchTo(*codePtr); + NextCase(); + + DeclCase(Jcnd_Tab): + if(auto jump = module->jumpMapV[*codePtr++].table.find(dataStk[1])) + { + dataStk.drop(); + BranchTo(*jump); + } + NextCase(); + + DeclCase(Jcnd_Tru): + if(dataStk.drop(), dataStk[0]) + BranchTo(*codePtr); + else + ++codePtr; + NextCase(); + + DeclCase(Jump_Lit): + BranchTo(*codePtr); + NextCase(); + + DeclCase(Jump_Stk): + dataStk.drop(); + BranchTo(dataStk[0] < module->jumpV.size() ? module->jumpV[dataStk[0]].codeIdx : 0); + NextCase(); + + //================================================ + // Push codes. + // + + DeclCase(Pfun_Lit): + if(*codePtr < module->functionV.size()) + dataStk.push(module->functionV[*codePtr]->idx); + else + dataStk.push(0); + ++codePtr; + NextCase(); + + DeclCase(Pstr_Stk): + if(dataStk[1] < module->stringV.size()) + dataStk[1] = ~module->stringV[dataStk[1]]->idx; + NextCase(); + + DeclCase(Push_GblArr): dataStk[1] = scopeGbl->arrV[*codePtr++].find(dataStk[1]); NextCase(); + DeclCase(Push_GblReg): dataStk.push(scopeGbl->regV[*codePtr++]); NextCase(); + DeclCase(Push_HubArr): dataStk[1] = scopeHub->arrV[*codePtr++].find(dataStk[1]); NextCase(); + DeclCase(Push_HubReg): dataStk.push(scopeHub->regV[*codePtr++]); NextCase(); + DeclCase(Push_Lit): dataStk.push(*codePtr++); NextCase(); + DeclCase(Push_LitArr): for(auto i = *codePtr++; i--;) dataStk.push(*codePtr++); NextCase(); + DeclCase(Push_LocArr): dataStk[1] = localArr[*codePtr++].find(dataStk[1]); NextCase(); + DeclCase(Push_LocReg): dataStk.push(localReg[*codePtr++]); NextCase(); + DeclCase(Push_ModArr): dataStk[1] = scopeMod->arrV[*codePtr++]->find(dataStk[1]); NextCase(); + DeclCase(Push_ModReg): dataStk.push(*scopeMod->regV[*codePtr++]); NextCase(); + + DeclCase(Push_StrArs): + dataStk.drop(); + dataStk[1] = scopeMap->getString(dataStk[1])->get(dataStk[0]); + NextCase(); + + //================================================ + // Script control codes. + // + + DeclCase(ScrDelay): + dataStk.drop(); + delay = dataStk[0]; + goto exec_intr; + + DeclCase(ScrDelay_Lit): + delay = *codePtr++; + goto exec_intr; + + DeclCase(ScrHalt): + state = ThreadState::Paused; + goto exec_intr; + + DeclCase(ScrRestart): + BranchTo(script->codeIdx); + NextCase(); + + DeclCase(ScrTerm): + goto thread_stop; + + DeclCase(ScrWaitI): + dataStk.drop(); + state = {ThreadState::WaitScrI, dataStk[0]}; + goto exec_intr; + + DeclCase(ScrWaitI_Lit): + state = {ThreadState::WaitScrI, *codePtr++}; + goto exec_intr; + + DeclCase(ScrWaitS): + dataStk.drop(); + state = {ThreadState::WaitScrS, dataStk[0]}; + goto exec_intr; + + DeclCase(ScrWaitS_Lit): + state = {ThreadState::WaitScrS, *codePtr++}; + goto exec_intr; + + //================================================ + // Stack control codes. + // + + DeclCase(Copy): + {auto temp = dataStk[1]; dataStk.push(temp);} + NextCase(); + + DeclCase(Swap): + std::swap(dataStk[2], dataStk[1]); + NextCase(); + + //================================================ + // Unary operator codes. + // + + OpSet(DecU); + OpSet(IncU); + + DeclCase(InvU): + dataStk[1] = ~dataStk[1]; + NextCase(); + + DeclCase(NegI): + dataStk[1] = ~dataStk[1] + 1; + NextCase(); + + DeclCase(NotU): + dataStk[1] = !dataStk[1]; + NextCase(); + } + + thread_stop: + stop(); + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Tracer.cpp b/src/acs/vm/ACSVM/Tracer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..901b09352dcd6592bb1c7e780645acf5f89bff51 --- /dev/null +++ b/src/acs/vm/ACSVM/Tracer.cpp @@ -0,0 +1,635 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Tracer classes. +// +//----------------------------------------------------------------------------- + +#include "Tracer.hpp" + +#include "BinaryIO.hpp" +#include "Code.hpp" +#include "CodeData.hpp" +#include "Environment.hpp" +#include "Error.hpp" +#include "Function.hpp" +#include "Jump.hpp" +#include "Module.hpp" +#include "Script.hpp" + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + // + // TracerACS0 constructor + // + TracerACS0::TracerACS0(Environment *env_, Byte const *data_, + std::size_t size_, bool compressed_) : + env {env_}, + codeFound {new Byte[size_]{}}, + codeIndex {new Word[size_]{}}, + codeC {0}, + jumpC {0}, + jumpMapC {0}, + data {data_}, + size {size_}, + compressed{compressed_} + { + } + + // + // TracerACS0 destructor + // + TracerACS0::~TracerACS0() + { + } + + // + // TracerACS0::getArgBytes + // + std::size_t TracerACS0::getArgBytes(CodeDataACS0 const *opData, std::size_t iter) + { + std::size_t argBytes; + + switch(opData->code) + { + case CodeACS0::Push_LitArrB: + if(size - iter < 1) throw ReadError(); + + return data[iter] + 1; + + case CodeACS0::Jcnd_Tab: + // Calculate alignment. + argBytes = ((iter + 3) & ~static_cast<std::size_t>(3)) - iter; + if(size - iter < argBytes) throw ReadError(); + + // Read the number of cases. + if(size - iter - argBytes < 4) throw ReadError(); + argBytes += ReadLE4(data + iter + argBytes) * 8 + 4; + + return argBytes; + + default: + argBytes = 0; + for(char const *s = opData->args; *s; ++s) switch(*s) + { + case 'B': argBytes += 1; break; + case 'H': argBytes += 2; break; + case 'W': argBytes += 4; break; + case 'b': argBytes += compressed ? 1 : 4; break; + case 'h': argBytes += compressed ? 2 : 4; break; + } + return argBytes; + } + } + + // + // TracerACS0::readCallFunc + // + std::pair<Word /*argc*/, Word /*func*/> TracerACS0::readCallFunc(std::size_t iter) + { + Word argc, func; + if(compressed) + { + argc = ReadLE1(data + iter + 0); + func = ReadLE2(data + iter + 1); + } + else + { + argc = ReadLE4(data + iter + 0); + func = ReadLE4(data + iter + 4); + } + return {argc, func}; + } + + // + // TracerACS0::readOpACS0 + // + std::tuple< + Word /*opCode*/, + CodeDataACS0 const * /*opData*/, + std::size_t /*opSize*/> + TracerACS0::readOpACS0(std::size_t iter) + { + if(compressed) + { + if(size - iter < 1) throw ReadError(); + + std::size_t opSize = 1; + Word opCode = data[iter]; + + if(opCode >= 240) + { + if(size - iter < 2) throw ReadError(); + ++opSize; + opCode = 240 + ((opCode - 240) << 8) + data[iter + 1]; + } + + return std::make_tuple(opCode, env->findCodeDataACS0(opCode), opSize); + } + else + { + if(size - iter < 4) throw ReadError(); + + Word opCode = ReadLE4(data + iter); + + return std::make_tuple(opCode, env->findCodeDataACS0(opCode), 4); + } + } + + // + // TracerACS0::setFound + // + bool TracerACS0::setFound(std::size_t first, std::size_t last) + { + Byte *begin = &codeFound[first]; + Byte *end = &codeFound[last]; + + std::size_t found = 0; + + for(Byte *itr = begin; itr != end; ++itr) + found += *itr; + + if(found) + { + if(found != last - first) throw ReadError(); + return false; + } + + for(Byte *itr = begin; itr != end; ++itr) + *itr = true; + + return true; + } + + // + // TracerACS0::trace + // + void TracerACS0::trace(Module *module) + { + // Add Kill to catch branches to zero. + codeC += 1 + env->getCodeData(Code::Kill)->argc; + + // Trace from entry points. + + for(Function *&func : module->functionV) + if(func && func->module == module) trace(func->codeIdx); + + for(Jump &jump : module->jumpV) + trace(jump.codeIdx); + + for(Script &scr : module->scriptV) + trace(scr.codeIdx); + + // Add Kill to catch execution past end. + codeC += 1 + env->getCodeData(Code::Kill)->argc; + } + + // + // TracerACS0::trace + // + void TracerACS0::trace(std::size_t iter) + { + for(std::size_t next;; iter = next) + { + // If at the end of the file, terminate tracer. Reaching here will + // result in a Kill, but the bytecode is otherwise well formed. + if(iter == size) return; + + // Whereas if iter is out of bounds, bytecode is malformed. + if(iter > size) throw ReadError(); + + // Read op. + CodeDataACS0 const *opData; + std::size_t opSize; + std::tie(std::ignore, opData, opSize) = readOpACS0(iter); + + // If no translation available, terminate trace. + if(!opData) + { + // Mark as found, so that the translator generates a KILL. + setFound(iter, iter + opSize); + codeC += 1 + env->getCodeData(Code::Kill)->argc; + return; + } + + std::size_t opSizeFull = opSize + getArgBytes(opData, iter + opSize); + + // If this op goes out of bounds, bytecode is malformed. + if(size - iter < opSizeFull) throw ReadError(); + + next = iter + opSizeFull; + + // If this op already found, terminate trace. + if(!setFound(iter, next)) + return; + + // Get data for translated op. + CodeData const *opTran = env->getCodeData(opData->transCode); + + // Count internal size of op. + switch(opData->code) + { + // -> Call(F) Drop_Nul() + case CodeACS0::Call_Nul: + codeC += 3; + break; + + case CodeACS0::CallSpec_1L: + case CodeACS0::CallSpec_2L: + case CodeACS0::CallSpec_3L: + case CodeACS0::CallSpec_4L: + case CodeACS0::CallSpec_5L: + case CodeACS0::CallSpec_6L: + case CodeACS0::CallSpec_7L: + case CodeACS0::CallSpec_8L: + case CodeACS0::CallSpec_9L: + case CodeACS0::CallSpec_10L: + case CodeACS0::CallSpec_1LB: + case CodeACS0::CallSpec_2LB: + case CodeACS0::CallSpec_3LB: + case CodeACS0::CallSpec_4LB: + case CodeACS0::CallSpec_5LB: + case CodeACS0::CallSpec_6LB: + case CodeACS0::CallSpec_7LB: + case CodeACS0::CallSpec_8LB: + case CodeACS0::CallSpec_9LB: + case CodeACS0::CallSpec_10LB: + case CodeACS0::Push_Lit2B: + case CodeACS0::Push_Lit3B: + case CodeACS0::Push_Lit4B: + case CodeACS0::Push_Lit5B: + case CodeACS0::Push_Lit6B: + case CodeACS0::Push_Lit7B: + case CodeACS0::Push_Lit8B: + case CodeACS0::Push_Lit9B: + case CodeACS0::Push_Lit10B: + codeC += opData->argc + 2; + break; + + case CodeACS0::Push_LitArrB: + codeC += data[iter + opSize] + 2; + break; + + // -> Push_Lit(0) Retn() + case CodeACS0::Retn_Nul: + codeC += 3; + break; + + case CodeACS0::CallFunc: + { + Word argc, func; + std::tie(argc, func) = readCallFunc(iter + opSize); + + FuncDataACS0 const *opFunc = env->findFuncDataACS0(func); + + if(!opFunc) + { + codeC += 1 + env->getCodeData(Code::Kill)->argc; + return; + } + + opTran = env->getCodeData(opFunc->getTransCode(argc)); + } + + default: + if(opTran->code == Code::CallFunc_Lit) + codeC += opData->argc + 1 + 2; + else + codeC += opTran->argc + 1; + + if(opTran->code == Code::Kill) + return; + + break; + } + + // Special handling for branching ops. + switch(opData->code) + { + case CodeACS0::Jcnd_Nil: + case CodeACS0::Jcnd_Tru: + ++jumpC; + trace(ReadLE4(data + iter + opSize)); + break; + + case CodeACS0::Jcnd_Lit: + ++jumpC; + trace(ReadLE4(data + iter + opSize + 4)); + break; + + case CodeACS0::Jcnd_Tab: + { + std::size_t count, jumpIter; + + jumpIter = (iter + opSize + 3) & ~static_cast<std::size_t>(3); + count = ReadLE4(data + jumpIter); jumpIter += 4; + + ++jumpMapC; + + // Trace all of the jump targets. + for(; count--; jumpIter += 8) + trace(ReadLE4(data + jumpIter + 4)); + } + break; + + case CodeACS0::Jump_Lit: + ++jumpC; + next = ReadLE4(data + iter + opSize); + break; + + case CodeACS0::Jump_Stk: + // The target of this jump will get traced when the dynamic jump + // targets get traced. + return; + + case CodeACS0::Retn_Stk: + case CodeACS0::Retn_Nul: + case CodeACS0::ScrTerm: + return; + + default: + break; + } + } + } + + // + // TracerACS0::translate + // + void TracerACS0::translate(Module *module) + { + std::unique_ptr<Word*[]> jumps{new uint32_t *[jumpC]}; + + Word *codeItr = module->codeV.data(); + Word **jumpItr = jumps.get(); + auto jumpMapItr = module->jumpMapV.data(); + + // Add Kill to catch branches to zero. + *codeItr++ = static_cast<Word>(Code::Kill); + *codeItr++ = static_cast<Word>(KillType::OutOfBounds); + *codeItr++ = 0; + + for(std::size_t iter = 0, next; iter != size; iter = next) + { + // If no code at this index, skip it. + if(!codeFound[iter]) + { + next = iter + 1; + continue; + } + + // Record jump target. + codeIndex[iter] = codeItr - module->codeV.data(); + + // Read op. + Word opCode; + CodeDataACS0 const *opData; + std::size_t opSize; + std::tie(opCode, opData, opSize) = readOpACS0(iter); + + // If no translation available, generate Kill. + if(!opData) + { + *codeItr++ = static_cast<Word>(Code::Kill); + *codeItr++ = static_cast<Word>(KillType::UnknownCode); + *codeItr++ = opCode; + next = iter + opSize; + continue; + } + + // Calculate next index. + next = iter + opSize + getArgBytes(opData, iter + opSize); + + // Get data for translated op. + CodeData const *opTran = env->getCodeData(opData->transCode); + + // Generate internal op. + switch(opData->code) + { + case CodeACS0::Call_Nul: + *codeItr++ = static_cast<Word>(opData->transCode); + if(compressed) + *codeItr++ = ReadLE1(data + iter + opSize); + else + *codeItr++ = ReadLE4(data + iter + opSize); + *codeItr++ = static_cast<Word>(Code::Drop_Nul); + break; + + case CodeACS0::CallSpec_1: + case CodeACS0::CallSpec_2: + case CodeACS0::CallSpec_3: + case CodeACS0::CallSpec_4: + case CodeACS0::CallSpec_5: + case CodeACS0::CallSpec_6: + case CodeACS0::CallSpec_7: + case CodeACS0::CallSpec_8: + case CodeACS0::CallSpec_9: + case CodeACS0::CallSpec_10: + case CodeACS0::CallSpec_5R1: + case CodeACS0::CallSpec_10R1: + *codeItr++ = static_cast<Word>(opData->transCode); + *codeItr++ = opData->stackArgC; + goto trans_args; + + case CodeACS0::CallSpec_1L: + case CodeACS0::CallSpec_1LB: + case CodeACS0::CallSpec_2L: + case CodeACS0::CallSpec_2LB: + case CodeACS0::CallSpec_3L: + case CodeACS0::CallSpec_3LB: + case CodeACS0::CallSpec_4L: + case CodeACS0::CallSpec_4LB: + case CodeACS0::CallSpec_5L: + case CodeACS0::CallSpec_5LB: + case CodeACS0::CallSpec_6L: + case CodeACS0::CallSpec_6LB: + case CodeACS0::CallSpec_7L: + case CodeACS0::CallSpec_7LB: + case CodeACS0::CallSpec_8L: + case CodeACS0::CallSpec_8LB: + case CodeACS0::CallSpec_9L: + case CodeACS0::CallSpec_9LB: + case CodeACS0::CallSpec_10L: + case CodeACS0::CallSpec_10LB: + *codeItr++ = static_cast<Word>(opData->transCode); + *codeItr++ = opData->argc - 1; + goto trans_args; + + case CodeACS0::Jcnd_Tab: + { + std::size_t count, jumpIter; + + jumpIter = (iter + opSize + 3) & ~static_cast<std::size_t>(3); + count = ReadLE4(data + jumpIter); jumpIter += 4; + + *codeItr++ = static_cast<Word>(opData->transCode); + *codeItr++ = jumpMapItr - module->jumpMapV.data(); + + (jumpMapItr++)->loadJumps(data + jumpIter, count); + } + break; + + case CodeACS0::Push_LitArrB: + *codeItr++ = static_cast<Word>(opData->transCode); + iter += opSize; + for(std::size_t n = *codeItr++ = data[iter++]; n--;) + *codeItr++ = data[iter++]; + break; + + case CodeACS0::Push_Lit2B: + case CodeACS0::Push_Lit3B: + case CodeACS0::Push_Lit4B: + case CodeACS0::Push_Lit5B: + case CodeACS0::Push_Lit6B: + case CodeACS0::Push_Lit7B: + case CodeACS0::Push_Lit8B: + case CodeACS0::Push_Lit9B: + case CodeACS0::Push_Lit10B: + *codeItr++ = static_cast<Word>(opData->transCode); + *codeItr++ = opData->argc; + goto trans_args; + + case CodeACS0::Retn_Nul: + *codeItr++ = static_cast<Word>(Code::Push_Lit); + *codeItr++ = static_cast<Word>(0); + *codeItr++ = static_cast<Word>(opData->transCode); + break; + + case CodeACS0::CallFunc: + { + Word argc, func; + std::tie(argc, func) = readCallFunc(iter + opSize); + + FuncDataACS0 const *opFunc = env->findFuncDataACS0(func); + + if(!opFunc) + { + *codeItr++ = static_cast<Word>(Code::Kill); + *codeItr++ = static_cast<Word>(KillType::UnknownFunc); + *codeItr++ = func; + continue; + } + + opTran = env->getCodeData(opFunc->getTransCode(argc)); + + *codeItr++ = static_cast<Word>(opTran->code); + if(opTran->code == Code::Kill) + { + *codeItr++ = static_cast<Word>(KillType::UnknownFunc); + *codeItr++ = func; + continue; + } + else if(opTran->code == Code::CallFunc) + { + *codeItr++ = argc; + *codeItr++ = opFunc->transFunc; + } + } + break; + + default: + *codeItr++ = static_cast<Word>(opData->transCode); + if(opTran->code == Code::Kill) + { + *codeItr++ = static_cast<Word>(KillType::UnknownCode); + *codeItr++ = opCode; + continue; + } + else if(opTran->code == Code::CallFunc) + { + *codeItr++ = opData->stackArgC; + *codeItr++ = opData->transFunc; + } + else if(opTran->code == Code::CallFunc_Lit) + { + *codeItr++ = opData->argc; + *codeItr++ = opData->transFunc; + } + + trans_args: + // Convert arguments. + iter += opSize; + for(char const *a = opData->args; *a; ++a) switch(*a) + { + case 'B': *codeItr++ = ReadLE1(data + iter); iter += 1; break; + case 'H': *codeItr++ = ReadLE2(data + iter); iter += 2; break; + case 'W': *codeItr++ = ReadLE4(data + iter); iter += 4; break; + + case 'J': + *jumpItr++ = codeItr - 1; + break; + + case 'S': + if(*(codeItr - 1) < module->stringV.size()) + *(codeItr - 1) = ~module->stringV[*(codeItr - 1)]->idx; + break; + + case 'b': + if(compressed) + {*codeItr++ = ReadLE1(data + iter); iter += 1;} + else + {*codeItr++ = ReadLE4(data + iter); iter += 4;} + break; + + case 'h': + if(compressed) + {*codeItr++ = ReadLE2(data + iter); iter += 2;} + else + {*codeItr++ = ReadLE4(data + iter); iter += 4;} + break; + } + + break; + } + } + + // Add Kill to catch execution past end. + *codeItr++ = static_cast<Word>(Code::Kill); + *codeItr++ = static_cast<Word>(KillType::OutOfBounds); + *codeItr++ = 1; + + // Translate jumps. Has to be done after code in order to jump forward. + while(jumpItr != jumps.get()) + { + codeItr = *--jumpItr; + + if(*codeItr < size) + *codeItr = codeIndex[*codeItr]; + else + *codeItr = 0; + } + + // Translate entry points. + + for(Function *&func : module->functionV) + { + if(func && func->module == module) + func->codeIdx = func->codeIdx < size ? codeIndex[func->codeIdx] : 0; + } + + for(Jump &jump : module->jumpV) + jump.codeIdx = jump.codeIdx < size ? codeIndex[jump.codeIdx] : 0; + + for(JumpMap &jumpMap : module->jumpMapV) + { + for(auto &jump : jumpMap.table) + jump.val = jump.val < size ? codeIndex[jump.val] : 0; + } + + for(Script &scr : module->scriptV) + scr.codeIdx = scr.codeIdx < size ? codeIndex[scr.codeIdx] : 0; + } +} + +// EOF + diff --git a/src/acs/vm/ACSVM/Tracer.hpp b/src/acs/vm/ACSVM/Tracer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d0918946a1501796244d01514ff390751a5fe423 --- /dev/null +++ b/src/acs/vm/ACSVM/Tracer.hpp @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Tracer classes. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Tracer_H__ +#define ACSVM__Tracer_H__ + +#include "Types.hpp" + +#include <memory> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // TracerACS0 + // + // Traces input bytecode in ACS0 format for code paths, and then + // translates discovered codes. + // + class TracerACS0 + { + public: + TracerACS0(Environment *env, Byte const *data, std::size_t size, bool compressed); + ~TracerACS0(); + + void trace(Module *module); + + void translate(Module *module); + + Environment *env; + + std::unique_ptr<Byte[]> codeFound; + std::unique_ptr<Word[]> codeIndex; + std::size_t codeC; + + std::size_t jumpC; + + std::size_t jumpMapC; + + private: + std::size_t getArgBytes(CodeDataACS0 const *opData, std::size_t iter); + + std::pair<Word /*argc*/, Word /*func*/> readCallFunc(std::size_t iter); + + std::tuple< + Word /*opCode*/, + CodeDataACS0 const * /*opData*/, + std::size_t /*opSize*/> + readOpACS0(std::size_t iter); + + bool setFound(std::size_t first, std::size_t last); + + void trace(std::size_t iter); + + // Bytecode information. + Byte const *data; + std::size_t size; + bool compressed; + }; +} + +#endif//ACSVM__Tracer_H__ + diff --git a/src/acs/vm/ACSVM/Types.hpp b/src/acs/vm/ACSVM/Types.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8da5445d570d176a0897071b8e86b9d1cfa70871 --- /dev/null +++ b/src/acs/vm/ACSVM/Types.hpp @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2017 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Common typedefs and class forward declarations. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Types_H__ +#define ACSVM__Types_H__ + +#include <cinttypes> +#include <cstddef> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // Host platform byte. + // Should this be uint8_t? Short answer: No. + // Long answer: char is the smallest addressable unit in the implementation. + // That is, it is by definition the byte of the target platform. And it is + // required to be at least 8 bits wide, which is the only real requirement + // for loading ACS bytecode. + // Furthermore, uint8_t is only defined if the implementation has a type + // with exactly 8 data bits and no padding bits. So even if you wanted to + // unilaterally declare bytes to be 8 bits, the only type that can possibly + // satisfy uint8_t is unsigned char. If CHAR_BIT is not 8, then there can be + // no uint8_t. + using Byte = unsigned char; + + using DWord = std::uint64_t; + using SDWord = std::int64_t; + using SWord = std::int32_t; + using Word = std::uint32_t; + + enum class Code; + enum class CodeACS0; + enum class Func; + enum class FuncACS0; + enum class InitTag; + enum class Signature : std::uint32_t; + class Array; + class ArrayInit; + class CodeData; + class CodeDataACS0; + class Environment; + class FuncDataACS0; + class Function; + class GlobalScope; + class HubScope; + class Jump; + class JumpMap; + class MapScope; + class Module; + class ModuleName; + class ModuleScope; + class PrintBuf; + class ScopeID; + class Script; + class ScriptAction; + class ScriptName; + class Serial; + class String; + class Thread; + class ThreadInfo; + class ThreadState; + class WordInit; + + using CallFunc = bool (*)(Thread *thread, Word const *argv, Word argc); +} + +#endif//ACSVM__Types_H__ + diff --git a/src/acs/vm/ACSVM/Vector.hpp b/src/acs/vm/ACSVM/Vector.hpp new file mode 100644 index 0000000000000000000000000000000000000000..966bc6ea9a45f805bd99d164d84359671c5327c4 --- /dev/null +++ b/src/acs/vm/ACSVM/Vector.hpp @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) 2015-2016 David Hill +// +// See COPYING for license information. +// +//----------------------------------------------------------------------------- +// +// Vector class. +// +//----------------------------------------------------------------------------- + +#ifndef ACSVM__Vector_H__ +#define ACSVM__Vector_H__ + +#include "Types.hpp" + +#include <new> +#include <utility> + + +//----------------------------------------------------------------------------| +// Types | +// + +namespace ACSVM +{ + // + // Vector + // + // Runtime sized array. + // + template<typename T> + class Vector + { + public: + using const_iterator = T const *; + using iterator = T *; + using size_type = std::size_t; + + + Vector() : dataV{nullptr}, dataC{0} {} + Vector(Vector<T> const &) = delete; + Vector(Vector<T> &&v) : dataV{v.dataV}, dataC{v.dataC} + {v.dataV = nullptr; v.dataC = 0;} + Vector(size_type count) : dataV{nullptr}, dataC{0} {alloc(count);} + + Vector(T const *v, size_type c) + { + dataC = c; + dataV = static_cast<T *>(::operator new(sizeof(T) * dataC)); + + for(T *itr = dataV, *last = itr + dataC; itr != last; ++itr) + new(itr) T{*v++}; + } + + ~Vector() {free();} + + T &operator [] (size_type i) {return dataV[i];} + + Vector<T> &operator = (Vector<T> &&v) {swap(v); return *this;} + + // + // alloc + // + template<typename... Args> + void alloc(size_type count, Args const &...args) + { + if(dataV) free(); + + dataC = count; + dataV = static_cast<T *>(::operator new(sizeof(T) * dataC)); + + for(T *itr = dataV, *last = itr + dataC; itr != last; ++itr) + new(itr) T{args...}; + } + + // begin + iterator begin() {return dataV;} + const_iterator begin() const {return dataV;} + + // data + T *data() {return dataV;} + + // end + iterator end() {return dataV + dataC;} + const_iterator end() const {return dataV + dataC;} + + // + // free + // + void free() + { + if(!dataV) return; + + for(T *itr = dataV + dataC; itr != dataV;) + (--itr)->~T(); + + ::operator delete(dataV); + dataV = nullptr; + dataC = 0; + } + + // + // realloc + // + template<typename... Args> + void realloc(size_type count, Args const &...args) + { + if(count == dataC) return; + + Vector<T> old{std::move(*this)}; + + dataC = count; + dataV = static_cast<T *>(::operator new(sizeof(T) * dataC)); + + T *itr = begin(), *last = end(), *oldItr = old.begin(); + T *mid = count > old.size() ? dataV + old.size() : last; + + while(itr != mid) + new(itr++) T{std::move(*oldItr++)}; + while(itr != last) + new(itr++) T{args...}; + } + + // size + size_type size() const {return dataC;} + + // swap + void swap(Vector<T> &v) + {std::swap(dataV, v.dataV); std::swap(dataC, v.dataC);} + + private: + T *dataV; + size_type dataC; + }; +} + +#endif//ACSVM__Vector_H__ + diff --git a/src/acs/vm/CMakeLists.txt b/src/acs/vm/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3116012c67ec1e89d47c5e3afbb3e17ad523c889 --- /dev/null +++ b/src/acs/vm/CMakeLists.txt @@ -0,0 +1,193 @@ +##----------------------------------------------------------------------------- +## +## Copyright (C) 2015-2016 David Hill +## +## See COPYING for license information. +## +##----------------------------------------------------------------------------- +## +## Root CMake file. +## +##----------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 2.6) + +cmake_policy(SET CMP0017 NEW) + +project(acsvm) + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + + +##----------------------------------------------------------------------------| +## Functions | +## + +## +## ACSVM_INSTALL_EXE +## +function(ACSVM_INSTALL_EXE name) + if(ACSVM_INSTALL_EXE) + install(TARGETS ${name} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) + endif() +endfunction() + +## +## ACSVM_INSTALL_LIB +## +function(ACSVM_INSTALL_LIB name) + if(ACSVM_INSTALL_LIB) + if(ACSVM_INSTALL_API) + install(TARGETS ${name} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) + elseif(ACSVM_SHARED) + install(TARGETS ${name} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ) + endif() + endif() +endfunction() + +## +## ACSVM_TRY_C_FLAG +## +function(ACSVM_TRY_C_FLAG flag name) + CHECK_C_COMPILER_FLAG(${flag} ACSVM_C_FLAG_${name}) + + if(ACSVM_C_FLAG_${name}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}" PARENT_SCOPE) + endif() +endfunction() + +## +## ACSVM_TRY_CXX_FLAG +## +function(ACSVM_TRY_CXX_FLAG flag name) + CHECK_CXX_COMPILER_FLAG(${flag} ACSVM_CXX_FLAG_${name}) + + if(ACSVM_CXX_FLAG_${name}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE) + endif() +endfunction() + + +##----------------------------------------------------------------------------| +## Environment Detection | +## + +set(ACSVM_SHARED_DEFAULT ON) + +if(NOT ACSVM_NOFLAGS) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + ACSVM_TRY_C_FLAG(-Wall Wall) + ACSVM_TRY_C_FLAG(-Wextra Wextra) + + ACSVM_TRY_C_FLAG(-std=c11 STD_C) + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + ACSVM_TRY_CXX_FLAG(-Wall Wall) + ACSVM_TRY_CXX_FLAG(-Wextra Wextra) + + ACSVM_TRY_CXX_FLAG(-std=c++11 STD_CXX) + endif() +endif() + +if(MSVC) + # Disable shared by default, as the source does not contain the needed + # declaration annotations to make that work under MSVC. + set(ACSVM_SHARED_DEFAULT OFF) +endif() + + +##----------------------------------------------------------------------------| +## Variables | +## + +## +## ACSVM_INSTALL_API +## +if(NOT DEFINED ACSVM_INSTALL_API) + set(ACSVM_INSTALL_API ON CACHE BOOL "Install ACSVM headers.") +endif() + +## +## ACSVM_INSTALL_DOC +## +if(NOT DEFINED ACSVM_INSTALL_DOC) + set(ACSVM_INSTALL_DOC ON CACHE BOOL "Install ACSVM documentation.") +endif() + +## +## ACSVM_INSTALL_EXE +## +if(NOT DEFINED ACSVM_INSTALL_EXE) + set(ACSVM_INSTALL_EXE ON CACHE BOOL "Install ACSVM executables.") +endif() + +## +## ACSVM_INSTALL_LIB +## +if(NOT DEFINED ACSVM_INSTALL_LIB) + set(ACSVM_INSTALL_LIB ON CACHE BOOL "Install ACSVM libraries.") +endif() + +## +## ACSVM_SHARED +## +## If true (or equiavalent), libraries will be built as SHARED. Otherwise, +## they are built as STATIC. +## +if(NOT DEFINED ACSVM_SHARED) + set(ACSVM_SHARED ${ACSVM_SHARED_DEFAULT} CACHE BOOL + "Build libraries as shared objects.") +endif() + +## +## ACSVM_SHARED_DECL +## +## Used internally for convenience in add_library commands. +## +if(ACSVM_SHARED) + set(ACSVM_SHARED_DECL SHARED) +else() + set(ACSVM_SHARED_DECL STATIC) +endif() + + +##----------------------------------------------------------------------------| +## Environment Configuration | +## + +include_directories(.) + + +##----------------------------------------------------------------------------| +## Targets | +## + +add_subdirectory(ACSVM) + +if(EXISTS "${CMAKE_SOURCE_DIR}/CAPI") + add_subdirectory(CAPI) +endif() + +if(EXISTS "${CMAKE_SOURCE_DIR}/Exec") + add_subdirectory(Exec) +endif() + +if(EXISTS "${CMAKE_SOURCE_DIR}/Util") + add_subdirectory(Util) +endif() + +## EOF + diff --git a/src/acs/vm/COPYING b/src/acs/vm/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..4362b49151d7b34ef83b3067a8f9c9f877d72a0e --- /dev/null +++ b/src/acs/vm/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/src/acs/vm/README b/src/acs/vm/README new file mode 100644 index 0000000000000000000000000000000000000000..bb24555639c4cd2db9be2627d5f9ee7b5f34145b --- /dev/null +++ b/src/acs/vm/README @@ -0,0 +1,121 @@ +ACS Virtual Machine (ACSVM) + +ACS VM library and standalone interpreter. Intended to be suitable for use in +Doom-based video game engines to implement Hexen ACS or ZDoom ACS bytecode +execution. It is focused on being usable for implementing existing extensions +and new functions (whether through new instructions or CallFunc indexes) while +having performance suitable to the complex ACS-based mods that have come into +existence. + + +=============================================================================== +Integrating ACSVM +=============================================================================== + +Although it can be used as an external library, ACSVM is also written so that +it can be integrated directly into the repository of projects using it without +needing to change any of the ACSVM files. + +It is enough to copy ACSVM's root into the root of the target repository, such +that acsvm/CMakeLists.txt exists. Only the subdirectories of desired components +(usually just ACSVM) need to be included. The CMakeLists.txt will automatically +disable the omitted components. + +In your root CMakeLists.txt, all that is needed is: + set(ACSVM_NOFLAGS ON) + set(ACSVM_SHARED OFF) + add_subdirectory(acsvm) +And the enabled components (again, usually just acsvm) will be available for +use in target_link_libraries. + + +=============================================================================== +Usage Overview +=============================================================================== + +=========================================================== +Getting Started +=========================================================== + +To use ACSVM, you will need to define a class that inherits from +ACSVM::Environment. By overriding the various virtuals you can configure the +different aspects of ACS loading and interpretation. But the absolute minimal +usage only requires overriding loadModule: + class Env : public ACSVM::Environment + { + protected: + virtual void loadModule(ACSVM::Module *module); + }; + +Which is implemented by using the module's name to locate the corresponding +bytecode and passing that to module->readBytecode. The default behavior of +getModuleName is to just set the ModuleName's string. This can be used to +implement bytecode directly from files: + void Env::loadModule(ACSVM::Module *module) + { + std::ifstream in{module->name.s->str, + std::ios_base::in | std::ios_base::binary}; + + if(!in) throw ACSVM::ReadError("file open failure"); + + std::vector<ACSVM::Byte> data; + + for(int c; c = in.get(), in;) + data.push_back(c); + + module->readBytecode(data.data(), data.size()); + } +In a Doom engine, this would most likely use lumps, instead. Either by doing +the lookup in loadModule, or by overriding getModuleName to turn the input +string into a lump number. + +To actually initialize the environment and load some modules, you can use: + void EnvInit(Environment &env, char const *const *namev, std::size_t namec) + { + // Load modules. + std::vector<ACSVM::Module *> modules; + for(std::size_t i = 1; i < namec; ++i) + modules.push_back(env.getModule(env.getModuleName(namev[i]))); + + // Create and activate scopes. + ACSVM::GlobalScope *global = env.getGlobalScope(0); global->active = true; + ACSVM::HubScope *hub = global->getHubScope(0); hub ->active = true; + ACSVM::MapScope *map = hub->getMapScope(0); map ->active = true; + + // Register modules with map scope. + map->addModules(modules.data(), modules.size()); + + // Start Open scripts. + map->scriptStartType(1, {}); + } + +And then a simple interpreter loop: + while(env.hasActiveThread()) + { + std::chrono::duration<double> rate{1.0 / 35}; + auto time = std::chrono::steady_clock::now() + rate; + + env.exec(); + + std::this_thread::sleep_until(time); + } +Note that if you already have a game loop, you only need to call env.exec once +per simulation frame. + +Finally, you will need to register instruction and callfunc functions to +actually interface with the larger environment. At the least, it is useful to +implement the EndPrint (86) instruction: + bool CF_EndPrint(ACSVM::Thread *thread, ACSVM::Word const *, ACSVM::Word) + { + std::cout << thread->printBuf.data() << '\n'; + thread->printBuf.drop(); + return false; + } + + Environment::Environment() + { + addCodeDataACS0(86, {"", 0, addCallFunc(CF_EndPrint)}); + } +Most of the other ACS printing logic is already handled by ACSVM, so this is +enough to display simple Print messages. + diff --git a/src/acs/vm/Util/CMakeLists.txt b/src/acs/vm/Util/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..081e078d2c053ccfaf5482e6e50f78c16804342b --- /dev/null +++ b/src/acs/vm/Util/CMakeLists.txt @@ -0,0 +1,38 @@ +##----------------------------------------------------------------------------- +## +## Copyright (C) 2015 David Hill +## +## See COPYING for license information. +## +##----------------------------------------------------------------------------- +## +## CMake file for acsvm-util. +## +##----------------------------------------------------------------------------- + + +##----------------------------------------------------------------------------| +## Environment Configuration | +## + +include_directories(.) + + +##----------------------------------------------------------------------------| +## Targets | +## + +## +## acsvm-capi +## +add_library(acsvm-util ${ACSVM_SHARED_DECL} + Floats.cpp + Floats.hpp +) + +target_link_libraries(acsvm-util acsvm) + +ACSVM_INSTALL_LIB(acsvm-util) + +## EOF + diff --git a/src/acs/vm/Util/Floats.cpp b/src/acs/vm/Util/Floats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7436e6ea4eb39b1b9ace76fce436d36271364015 --- /dev/null +++ b/src/acs/vm/Util/Floats.cpp @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//---------------------------------------------------------------------------- +// +// Floating-point utilities. +// +//---------------------------------------------------------------------------- + +#include "Floats.hpp" + +#include "ACSVM/Thread.hpp" + +#include <cstdio> + + +//----------------------------------------------------------------------------| +// Macros | +// + +// +// DoubleOp +// +#define DoubleOp(name, op) \ + bool CF_##name##F_W2(Thread *thread, Word const *argV, Word) \ + { \ + double l = WordsToFloat<double, 2>({{argV[0], argV[1]}}); \ + double r = WordsToFloat<double, 2>({{argV[2], argV[3]}}); \ + for(auto w : FloatToWords<2>(l op r)) thread->dataStk.push(w); \ + return false; \ + } + +// +// FloatOp +// +#define FloatOp(name, op) \ + bool CF_##name##F_W1(Thread *thread, Word const *argV, Word) \ + { \ + float l = WordsToFloat<float, 1>({{argV[0]}}); \ + float r = WordsToFloat<float, 1>({{argV[1]}}); \ + for(auto w : FloatToWords<1>(l op r)) thread->dataStk.push(w); \ + return false; \ + } + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + FloatOp(Add, +); + FloatOp(Div, /); + FloatOp(Mul, *); + FloatOp(Sub, -); + + DoubleOp(Add, +); + DoubleOp(Div, /); + DoubleOp(Mul, *); + DoubleOp(Sub, -); + + // + // void PrintDouble(double f) + // + bool CF_PrintDouble(Thread *thread, Word const *argV, Word) + { + double f = WordsToFloat<double, 2>({{argV[0], argV[1]}}); + thread->printBuf.reserve(std::snprintf(nullptr, 0, "%f", f)); + thread->printBuf.format("%f", f); + return false; + } + + // + // void PrintFloat(float f) + // + bool CF_PrintFloat(Thread *thread, Word const *argV, Word) + { + float f = WordsToFloat<float, 1>({{argV[0]}}); + thread->printBuf.reserve(std::snprintf(nullptr, 0, "%f", f)); + thread->printBuf.format("%f", f); + return false; + } +} + +// EOF + diff --git a/src/acs/vm/Util/Floats.hpp b/src/acs/vm/Util/Floats.hpp new file mode 100644 index 0000000000000000000000000000000000000000..94cd9b25ebb6b3a1a82af7d9aec6cc3413315442 --- /dev/null +++ b/src/acs/vm/Util/Floats.hpp @@ -0,0 +1,186 @@ +//---------------------------------------------------------------------------- +// +// Copyright (C) 2015 David Hill +// +// See COPYING for license information. +// +//---------------------------------------------------------------------------- +// +// Floating-point utilities. +// +//---------------------------------------------------------------------------- + +#ifndef ACSVM__Util__Floats_H__ +#define ACSVM__Util__Floats_H__ + +#include "../ACSVM/Types.hpp" + +#include <array> +#include <cmath> + + +//----------------------------------------------------------------------------| +// Extern Functions | +// + +namespace ACSVM +{ + bool CF_AddF_W1(Thread *thread, Word const *argV, Word argC); + bool CF_DivF_W1(Thread *thread, Word const *argV, Word argC); + bool CF_MulF_W1(Thread *thread, Word const *argV, Word argC); + bool CF_SubF_W1(Thread *thread, Word const *argV, Word argC); + bool CF_AddF_W2(Thread *thread, Word const *argV, Word argC); + bool CF_DivF_W2(Thread *thread, Word const *argV, Word argC); + bool CF_MulF_W2(Thread *thread, Word const *argV, Word argC); + bool CF_SubF_W2(Thread *thread, Word const *argV, Word argC); + + bool CF_PrintDouble(Thread *thread, Word const *argV, Word argC); + bool CF_PrintFloat(Thread *thread, Word const *argV, Word argC); + + // + // FloatExpBitDefault + // + template<std::size_t N> + constexpr std::size_t FloatExpBitDefault(); + + template<> constexpr std::size_t FloatExpBitDefault<1>() {return 8;} + template<> constexpr std::size_t FloatExpBitDefault<2>() {return 11;} + template<> constexpr std::size_t FloatExpBitDefault<4>() {return 15;} + + // + // FloatToWords + // + template<std::size_t N, std::size_t ExpBit = FloatExpBitDefault<N>(), typename FltT> + std::array<Word, N> FloatToWords(FltT const &f) + { + static_assert(N >= 1, "N must be at least 1."); + static_assert(ExpBit >= 2, "ExpBit must be at least 2."); + static_assert(ExpBit <= 30, "ExpBit must be at most 30."); + + + constexpr int ExpMax = (1 << (ExpBit - 1)) - 1; + constexpr int ExpMin = -ExpMax - 1; + constexpr int ExpOff = ExpMax - 1; + + + std::array<Word, N> w{}; + Word &wHi = std::get<N - 1>(w); + Word &wLo = std::get<0>(w); + + // Convert sign. + bool sigRaw = std::signbit(f); + wHi = static_cast<Word>(sigRaw) << 31; + + // Convert special values. + switch(std::fpclassify(f)) + { + case FP_INFINITE: + // Convert to +/-INFINITY. + wHi |= (0xFFFFFFFFu << (31 - ExpBit)) & 0x7FFFFFFF; + return w; + + case FP_NAN: + // Convert to NAN. + wHi |= 0x7FFFFFFF; + for(auto itr = &wLo, end = &wHi; itr != end; ++itr) + *itr = 0xFFFFFFFF; + return w; + + case FP_SUBNORMAL: + // TODO: Subnormals. + case FP_ZERO: + // Convert to +/-0. + return w; + } + + int expRaw = 0; + FltT manRaw = std::ldexp(std::frexp(std::fabs(f), &expRaw), N * 32 - ExpBit); + + // Check for exponent overflow. + if(expRaw > ExpMax) + { + // Overflow to +/-INFINITY. + wHi |= (0xFFFFFFFFu << (32 - ExpBit)) & 0x7FFFFFFF; + return w; + } + + // Check for exponent underflow. + if(expRaw < ExpMin) + { + // Underflow to +/-0. + return w; + } + + // Convert exponent. + wHi |= static_cast<Word>(expRaw + ExpOff) << (32 - ExpBit - 1); + + // Convert mantissa. + for(int i = 0, e = N - 1; i != e; ++i) + w[i] = static_cast<Word>(std::fmod(std::ldexp(manRaw, -i * 32), 4294967296.0)); + wHi |= static_cast<Word>(std::ldexp(manRaw, -static_cast<int>(N - 1) * 32)) + & ~(0xFFFFFFFFu << (31 - ExpBit)); + + return w; + } + + // + // WordsToFloat + // + template<typename FltT, std::size_t N, std::size_t ExpBit = FloatExpBitDefault<N>()> + FltT WordsToFloat(std::array<Word, N> const &w) + { + static_assert(N >= 1, "N must be at least 1."); + static_assert(ExpBit >= 2, "ExpBit must be at least 2."); + static_assert(ExpBit <= 30, "ExpBit must be at most 30."); + + + constexpr int ExpMax = (1 << (ExpBit - 1)) - 1; + constexpr int ExpMin = -ExpMax - 1; + constexpr int ExpOff = ExpMax; + + constexpr Word ManMask = 0xFFFFFFFFu >> (ExpBit + 1); + + + Word const &wHi = std::get<N - 1>(w); + Word const &wLo = std::get<0>(w); + + bool sig = !!(wHi & 0x80000000); + int exp = static_cast<int>((wHi & 0x7FFFFFFF) >> (31 - ExpBit)) - ExpOff; + + // INFINITY or NAN. + if(exp > ExpMax) + { + // Check for NAN. + for(auto itr = &wLo, end = &wHi; itr != end; ++itr) + if(*itr) return NAN; + if(wHi & ManMask) return NAN; + + return sig ? -INFINITY : +INFINITY; + } + + // Zero or subnormal. + if(exp < ExpMin) + { + // TODO: Subnormals. + + return sig ? -0.0f : +0.0f; + } + + // Convert mantissa. + FltT f = 0; + for(auto itr = &wHi, end = &wLo; itr != end;) + f = ldexp(f + *--itr, -32); + f = ldexp(f + (wHi & ManMask) + (ManMask + 1), -static_cast<int>(31 - ExpBit)); + + // Convert exponent. + f = ldexp(f, exp); + + // Convert sign. + f = sig ? -f : +f; + + return f; + } +} + +#endif//ACSVM__Util__Floats_H__ + diff --git a/src/acs/vm/doc/ACSVM.txt b/src/acs/vm/doc/ACSVM.txt new file mode 100644 index 0000000000000000000000000000000000000000..17837859be145e38bc0e15f07dff41c1865ec0a9 --- /dev/null +++ b/src/acs/vm/doc/ACSVM.txt @@ -0,0 +1,1212 @@ +############################################################################### +ACSVM Library Specification +############################################################################### + +=============================================================================== +Types <ACSVM/ACSVM/Types.hpp> +=============================================================================== + +Synopsis: + using Byte = unsigned char; + + using DWord = std::uint64_t; + using SDWord = std::int64_t; + using SWord = std::int32_t; + using Word = std::uint32_t; + + using CallFunc = bool (*)(Thread *thread, Word const *argv, Word argc); + +=============================================================================== +Deferred Actions <ACSVM/ACSVM/Action.hpp> +=============================================================================== + +=========================================================== +ACSVM::ScopeID +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Action.hpp> + class ScopeID + { + public: + ScopeID() = default; + ScopeID(Word global, Word hub, Word map); + + bool operator == (ScopeID const &id) const; + bool operator != (ScopeID const &id) const; + + Word global; + Word hub; + Word map; + }; + +=============================================================================== +Arrays <ACSVM/ACSVM/Array.hpp> +=============================================================================== + +=========================================================== +ACSVM::Array +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Array.hpp> + class Array + { + public: + Array(); + Array(Array const &) = delete; + Array(Array &&array); + ~Array(); + + Word &operator [] (Word idx); + + void clear(); + + Word find(Word idx) const; + + void lockStrings(Environment *env) const; + + void unlockStrings(Environment *env) const; + }; + +=============================================================================== +Codes <ACSVM/ACSVM/Code.hpp> +=============================================================================== + +=========================================================== +ACSVM::Code +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Code.hpp> + enum class Code + { + /* ... */ + None + }; + +=========================================================== +ACSVM::Func +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Code.hpp> + enum class Func + { + /* ... */ + None + }; + +=========================================================== +ACSVM::KillType +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Code.hpp> + enum class KillType + { + None, + OutOfBounds, + UnknownCode, + UnknownFunc, + BranchLimit, + }; + +=============================================================================== +Code Data <ACSVM/ACSVM/CodeData.hpp> +=============================================================================== + +=========================================================== +ACSVM::CodeDataACS0 +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/CodeData.hpp> + class CodeDataACS0 + { + public: + CodeDataACS0(char const *args, Code transCode, Word stackArgC, + Word transFunc = 0); + CodeDataACS0(char const *args, Word stackArgC, Word transFunc); + + char const *args; + std::size_t argc; + + Word stackArgC; + + Code transCode; + + Word transFunc; + }; + +=========================================================== +ACSVM::FuncDataACS0 +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/CodeData.hpp> + class FuncDataACS0 + { + public: + using TransCode = std::pair<Word, Code>; + + + FuncDataACS0(FuncDataACS0 const &data); + FuncDataACS0(FuncDataACS0 &&data); + FuncDataACS0(Word transFunc); + FuncDataACS0(Word transFunc, std::initializer_list<TransCode> transCodes); + ~FuncDataACS0(); + + FuncDataACS0 &operator = (FuncDataACS0 const &) = delete; + FuncDataACS0 &operator = (FuncDataACS0 &&data); + + Word transFunc; + }; + +=============================================================================== +Environment <ACSVM/ACSVM/Environment.hpp> +=============================================================================== + +=========================================================== +ACSVM::Environment +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Environment.hpp> + class Environment + { + public: + Environment(); + virtual ~Environment(); + + Word addCallFunc(CallFunc func); + + void addCodeDataACS0(Word code, CodeDataACS0 &&data); + + void addFuncDataACS0(Word func, FuncDataACS0 &&data); + + virtual bool checkLock(Thread *thread, Word lock, bool door); + + virtual bool checkTag(Word type, Word tag); + + void collectStrings(); + + virtual void exec(); + + void freeGlobalScope(GlobalScope *scope); + + void freeModule(Module *module); + + GlobalScope *getGlobalScope(Word id); + + Module *getModule(ModuleName const &name); + + ModuleName getModuleName(char const *str); + virtual ModuleName getModuleName(char const *str, std::size_t len); + + String *getString(Word idx); + String *getString(char const *first, char const *last); + String *getString(char const *str); + String *getString(char const *str, std::size_t len); + String *getString(StringData const *data); + + bool hasActiveThread() const; + + virtual void loadState(Serial &in); + + virtual void printArray(PrintBuf &buf, Array const &array, Word index, + Word limit); + + virtual void printKill(Thread *thread, Word type, Word data); + + virtual ModuleName readModuleName(Serial &in) const; + + String *readString(Serial &in) const; + + virtual void refStrings(); + + virtual void resetStrings(); + + virtual void saveState(Serial &out) const; + + virtual void writeModuleName(Serial &out, ModuleName const &name) const; + + void writeString(Serial &out, String const *in) const; + + StringTable stringTable; + + Word scriptLocRegC; + + + static void PrintArrayChar(PrintBuf &buf, Array const &array, Word index, + Word limit); + + static void PrintArrayUTF8(PrintBuf &buf, Array const &array, Word index, + Word limit); + + static constexpr Word ScriptLocRegCDefault; + + protected: + virtual Thread *allocThread(); + + virtual Word callSpecImpl(Thread *thread, Word spec, Word const *argV, + Word argC); + + virtual void loadModule(Module *module) = 0; + }; + +Description: + Represents an execution environment. + +----------------------------------------------------------- +ACSVM::Environment::Environment +----------------------------------------------------------- + +Synopsis: + Environment(); + +Description: + Constructs the Environment object. + +----------------------------------------------------------- +ACSVM::Environment::~Environment +----------------------------------------------------------- + +Synopsis: + ~Environment(); + +Description: + Destructs the Environment object. + +----------------------------------------------------------- +ACSVM::Environment::addCallFunc +----------------------------------------------------------- + +Synopsis: + Word addCallFunc(CallFunc func); + +Description: + Adds a function callback for scripts and returns its index. + +Returns: + Added function's index. + +----------------------------------------------------------- +ACSVM::Environment::addCodeDataACS0 +----------------------------------------------------------- + +Synopsis: + void addCodeDataACS0(Word code, CodeDataACS0 &&data); + +Description: + Adds a translation from instruction code for ACS0 and derived bytecode. + +----------------------------------------------------------- +ACSVM::Environment::addFuncDataACS0 +----------------------------------------------------------- + +Synopsis: + void addFuncDataACS0(Word func, FuncDataACS0 &&data); + +Description: + Adds a translation from callfunc func for ACS0 and derived bytecode. + +----------------------------------------------------------- +ACSVM::Environment::checkLock +----------------------------------------------------------- + +Synopsis: + virtual bool checkLock(Thread *thread, Word lock, bool door); + +Description: + Called to check if a given lock number can be used from a given thread. The + lock number has no internal semantics, and is passed from the user source + unaltered. + + The base implementation always return false. + +Returns: + True if the lock is open for that thread, false otherwise. + +----------------------------------------------------------- +ACSVM::Environment::checkTag +----------------------------------------------------------- + +Synopsis: + virtual bool checkTag(Word type, Word tag); + +Description: + Called to check if a given tag is inactive. The tag type and number both have + no internal semantics, and are passed from the user source unaltered. + + The base implementation always returns false. + +Returns: + True if the tag is inactive, false otherwise. + +----------------------------------------------------------- +ACSVM::Environment::collectStrings +----------------------------------------------------------- + +Synopsis: + void collectStrings(); + +Description: + Performs a full scan of the environment and frees strings that are no longer + in use. + +----------------------------------------------------------- +ACSVM::Environment::exec +----------------------------------------------------------- + +Synopsis: + virtual void exec(); + +Description: + Performs a single execution cycle. Deferred script actions will be applied, + and active threads will execute until they terminate or enter a wait state. + +----------------------------------------------------------- +ACSVM::Environment::freeGlobalScope +----------------------------------------------------------- + +Synopsis: + void freeGlobalScope(GlobalScope *scope); + +Description: + Destructs and deallocates a contained GlobalScope object. + +----------------------------------------------------------- +ACSVM::Environment::freeModule +----------------------------------------------------------- + +Synopsis: + void freeModule(Module *module); + +Description: + Destructs and deallocates a contained Module object. + + If any other modules reference the freed module, they must also be freed. + +----------------------------------------------------------- +ACSVM::Environment::getGlobalScope +----------------------------------------------------------- + +Synopsis: + GlobalScope *getGlobalScope(Word id); + +Description: + Retrieves a GlobalScope object by its identifier number. If it does not + exist, it will be created. + +Returns: + GlobalScope object with given id. + +----------------------------------------------------------- +ACSVM::Environment::getModule +----------------------------------------------------------- + +Synopsis: + Module *getModule(ModuleName const &name); + +Description: + Retrieves a Module object by name. If it does not exist or is not loaded, it + will be created and loaded as needed. + +Returns: + Module object with given name. + +----------------------------------------------------------- +ACSVM::Environment::getModuleName +----------------------------------------------------------- + +Synopsis: + ModuleName getModuleName(char const *str); + virtual ModuleName getModuleName(char const *str, std::size_t len); + +Description: + Generates a ModuleName from an input string. The first form calls the second, + using the null-terminated length of the input string. + + The base implementation converts the input string into a String object for + ModuleName::s, leaving the other ModuleName fields set to 0. + +Returns: + ModuleName object formed from input string. + +----------------------------------------------------------- +ACSVM::Environment::getScriptType +----------------------------------------------------------- + +Synopsis: + virtual std::pair<Word, Word> getScriptTypeACS0(Word name); + virtual Word getScriptTypeACSE(Word type); + +Description: + Translates a bytecode script type into an internal type. + + First form takes the script number and must return the type and name. + + The base implementation of the first form translates by dividing by 1000. The + second form returns the type unaltered. + +Returns: + Translated script type or (type, name) pair. + +----------------------------------------------------------- +ACSVM::Environment::getString +----------------------------------------------------------- + +Synopsis: + String *getString(Word idx); + String *getString(char const *first, char const *last); + String *getString(char const *str); + String *getString(char const *str, std::size_t len); + String *getString(StringData const *data); + +Description: + First form returns a String object as if by calling (&stringTable[~idx]). + + Second, third, and fourth forms create a StringData from input to find or + create an entry in stringTable. + + Fifth form uses the supplied StringData object to find or create an entry in + stringTable. If data is null, null is returned. This is intended primarily + for resetting strings after deserialization. + +Returns: + First form returns the String object with the given index. All other forms + return a String object with the same data as the input. + + Fifth form will return null if input is null, and non-null otherwise. All + other forms never return null. + +----------------------------------------------------------- +ACSVM::Environment::hasActiveThread +----------------------------------------------------------- + +Synopsis: + bool hasActiveThread() const; + +Description: + Checks for any active threads. A thread is considered active if it has any + state other than ThreadState::Inactive. So this will include threads that are + delayed, waiting for a condition (script or tag), or set to stop during the + next execution cycle. + +Returns: + True if there are any active threads, false otherwise. + +----------------------------------------------------------- +ACSVM::Environment::loadState +----------------------------------------------------------- + +Synopsis: + virtual void loadState(Serial &in); + +Description: + Restores the environment state from a previously serialized instance. If in + does not contain a byte stream generated by a previous call to saveState, the + behavior is undefined. + +----------------------------------------------------------- +ACSVM::Environment::printArray +----------------------------------------------------------- + +Synopsis: + virtual void printArray(PrintBuf &buf, Array const &array, Word index, + Word limit); + +Description: + Called to write a null-terminated character subsequence from an Array object + to a print buffer. + + The base implementation calls PrintArrayChar. + +----------------------------------------------------------- +ACSVM::Environment::printKill +----------------------------------------------------------- + +Synopsis: + virtual void printKill(Thread *thread, Word type, Word data); + +Description: + Called when a thread encounters an error and must terminate. This includes + executing Code::Kill or calling Func::Kill. When calling by ACSVM itself, the + type parameter has a value from the KillType enumeration. + + This function is expected to return normally, and the caller will handle + thread termination. + + The base implementation prints kill information to std::cerr. + +----------------------------------------------------------- +ACSVM::Environment::readModuleName +----------------------------------------------------------- + +Synopsis: + virtual ModuleName readModuleName(Serial &in) const; + +Description: + Called to read a ModuleName from a serialized environment. + + The base implementation reads the s and i members, leaving the p member null. + +Returns: + Deserialized ModuleName. + +----------------------------------------------------------- +ACSVM::Environment::readString +----------------------------------------------------------- + +Synopsis: + String *readString(Serial &in) const; + +Description: + Reads a String by index from a serialized Environment. If the written String + pointer was null, this function returns a null pointer. + +Returns: + Deserialized String. + +----------------------------------------------------------- +ACSVM::Environment::refStrings +----------------------------------------------------------- + +Synopsis: + virtual void refStrings(); + +Description: + Called by collectStrings to mark contained strings as referenced. + + The base implementation marks strings of all contained objects, as well as + performs an exhaustive scan of VM memory for string indexes. + +----------------------------------------------------------- +ACSVM::Environment::resetStrings +----------------------------------------------------------- + +Synopsis: + virtual void resetStrings(); + +Description: + Called by loadState after reading the new StringTable to reset any String + pointers to the corresponding entry in the read table. + + The base implementation resets strings of all contained objects. + +----------------------------------------------------------- +ACSVM::Environment::saveState +----------------------------------------------------------- + +Synopsis: + virtual void saveState(Serial &out) const; + +Description: + Serializes the environment state, which can be restored with a call to + loadState. + +----------------------------------------------------------- +ACSVM::Environment::writeModuleName +----------------------------------------------------------- + +Synopsis: + virtual void writeModuleName(Serial &out, + ModuleName const &name) const; + +Description: + Called to write a ModuleName. + + The base implementation writes the s and i members. + +----------------------------------------------------------- +ACSVM::Environment::writeString +----------------------------------------------------------- + +Synopsis: + void writeString(Serial &out, String const *in) const; + +Description: + Writes a String by index. If in is null, a null pointer will be returned by + the corresponding call to readString. + +=============================================================================== +Errors <ACSVM/ACSVM/Error.hpp> +=============================================================================== + +=========================================================== +ACSVM::ReadError +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Error.hpp> + class ReadError : public std::exception + { + public: + ReadError(char const *msg = "ACSVM::ReadError"); + + virtual char const *what() const noexcept; + }; + +=============================================================================== +Modules <ACSVM/ACSVM/Module.hpp> +=============================================================================== + +=========================================================== +ACSVM::ModuleName +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Module.hpp> + class ModuleName + { + public: + ModuleName(String *s, void *p, std::size_t i); + + bool operator == (ModuleName const &name) const; + bool operator != (ModuleName const &name) const; + + std::size_t hash() const; + + String *s; + void *p; + std::size_t i; + }; + +=========================================================== +ACSVM::Module +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Module.hpp> + class Module + { + public: + void readBytecode(Byte const *data, std::size_t size); + + Environment *env; + ModuleName name; + + bool isACS0; + bool loaded; + + + static constexpr std::uint32_t ChunkID(char c0, char c1, char c2, char c3); + + static constexpr std::uint32_t ChunkID(char const (&s)[5]); + }; + +=============================================================================== +Print Buffers <ACSVM/ACSVM/PrintBuf.hpp> +=============================================================================== + +=========================================================== +ACSVM::PrintBuf +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/PrintBuf.hpp> + class PrintBuf + { + public: + PrintBuf(); + ~PrintBuf(); + + void clear(); + + char const *data() const; + char const *dataFull() const; + + void drop(); + + void format(char const *fmt, ...); + void formatv(char const *fmt, std::va_list arg); + + char *getBuf(std::size_t count); + + char *getLoadBuf(std::size_t countFull, std::size_t count); + + void push(); + + void put(char c); + void put(char const *s); + void put(char const *s, std::size_t n); + + void reserve(std::size_t count); + + std::size_t size() const; + std::size_t sizeFull() const; + }; + +=============================================================================== +Scopes <ACSVM/ACSVM/Scope.hpp> +=============================================================================== + +=========================================================== +ACSVM::GlobalScope +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Scope.hpp> + class GlobalScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + void freeHubScope(HubScope *scope); + + HubScope *getHubScope(Word id); + + bool hasActiveThread() const; + + void lockStrings() const; + + void reset(); + + void unlockStrings() const; + + Environment *const env; + Word const id; + + Array arrV[ArrC]; + Word regV[RegC]; + + bool active; + }; + +=========================================================== +ACSVM::HubScope +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Scope.hpp> + class HubScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + void freeMapScope(MapScope *scope); + + MapScope *getMapScope(Word id); + + bool hasActiveThread() const; + + void lockStrings() const; + + void reset(); + + void unlockStrings() const; + + Environment *const env; + GlobalScope *const global; + Word const id; + + Array arrV[ArrC]; + Word regV[RegC]; + + bool active; + }; + +=========================================================== +ACSVM::MapScope +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Scope.hpp> + class MapScope + { + public: + using ScriptStartFunc = void (*)(Thread *); + using ScriptStartFuncC = MapScope_ScriptStartFuncC; + + class ScriptStartInfo; + + + void addModules(Module *const *moduleV, std::size_t moduleC); + + Script *findScript(ScriptName name); + + ModuleScope *getModuleScope(Module *module); + + String *getString(Word idx) const; + + bool hasActiveThread() const; + + bool hasModules() const; + + bool isScriptActive(Script *script); + + void loadState(Serial &in); + + void lockStrings() const; + + void reset(); + + void saveState(Serial &out) const; + + bool scriptPause(Script *script); + bool scriptPause(ScriptName name, ScopeID scope); + bool scriptStart(Script *script, ScriptStartInfo const &info); + bool scriptStart(ScriptName name, ScopeID scope, + ScriptStartInfo const &info); + bool scriptStartForced(Script *script, ScriptStartInfo const &info); + bool scriptStartForced(ScriptName name, ScopeID scope, + ScriptStartInfo const &info); + Word scriptStartResult(Script *script, ScriptStartInfo const &info); + Word scriptStartResult(ScriptName name, ScriptStartInfo const &info); + Word scriptStartType(Word type, ScriptStartInfo const &info); + Word scriptStartTypeForced(Word type, ScriptStartInfo const &info); + bool scriptStop(Script *script); + bool scriptStop(ScriptName name, ScopeID scope); + + void unlockStrings() const; + + Environment *const env; + HubScope *const hub; + Word const id; + + bool active; + bool clampCallSpec; + }; + +=========================================================== +ACSVM::MapScope::ScriptStartInfo +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Scope.hpp> + class ScriptStartInfo + { + public: + ScriptStartInfo(); + ScriptStartInfo(Word const *argV, std::size_t argC, + ThreadInfo const *info = nullptr, ScriptStartFunc func = nullptr); + ScriptStartInfo(Word const *argV, std::size_t argC, + ThreadInfo const *info, ScriptStartFuncC func); + + Word const *argV; + ScriptStartFunc func; + ScriptStartFuncC funcc; + ThreadInfo const *info; + std::size_t argC; + }; + +=========================================================== +ACSVM::ModuleScope +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Scope.hpp> + class ModuleScope + { + public: + static constexpr std::size_t ArrC = 256; + static constexpr std::size_t RegC = 256; + + + void lockStrings() const; + + void unlockStrings() const; + + Environment *const env; + MapScope *const map; + Module *const module; + + Array *arrV[ArrC]; + Word *regV[RegC]; + }; + +=============================================================================== +Scripts <ACSVM/ACSVM/Script.hpp> +=============================================================================== + +=========================================================== +ACSVM::ScriptName +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Script.hpp> + class ScriptName + { + public: + ScriptName(); + ScriptName(String *s); + ScriptName(String *s, Word i); + ScriptName(Word i); + + String *s; + Word i; + }; + +=========================================================== +ACSVM::Script +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Script.hpp> + class Script + { + public: + explicit Script(Module *module); + ~Script(); + + Module *const module; + + ScriptName name; + + Word argC; + Word codeIdx; + Word flags; + Word locArrC; + Word locRegC; + Word type; + + bool flagClient : 1; + bool flagNet : 1; + }; + +=============================================================================== +Stacks <ACSVM/ACSVM/Stack.hpp> +=============================================================================== + +=========================================================== +ACSVM::Stack +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Stack.hpp> + template<typename T> + class Stack + { + public: + Stack(); + ~Stack(); + + T &operator [] (std::size_t idx); + + T *begin(); + T const *begin() const; + + void clear(); + + void drop(); + void drop(std::size_t n); + + bool empty() const; + + T *end(); + T const *end() const; + + void push(T const &value); + void push(T &&value); + + void reserve(std::size_t count); + + std::size_t size() const; + }; + +=============================================================================== +Locals Storage <ACSVM/ACSVM/Store.hpp> +=============================================================================== + +=========================================================== +ACSVM::Store +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Store.hpp> + template<typename T> + class Store + { + public: + Store(); + ~Store(); + + T &operator [] (std::size_t idx); + + void alloc(std::size_t count); + + void allocLoad(std::size_t countFull, std::size_t count); + + T *begin(); + T const *begin() const; + + T *beginFull(); + T const *beginFull() const; + + void clear(); + + T const *dataFull() const; + + T *end(); + T const *end() const; + + void free(std::size_t count); + + std::size_t size() const; + + std::size_t sizeFull() const; + }; + +=============================================================================== +Strings <ACSVM/ACSVM/String.hpp> +=============================================================================== + +=========================================================== +ACSVM::StringData +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/String.hpp> + class StringData + { + public: + StringData(char const *first, char const *last); + StringData(char const *str, std::size_t len); + StringData(char const *str, std::size_t len, std::size_t hash); + + bool operator == (StringData const &r) const; + + char const *const str; + std::size_t const len; + std::size_t const hash; + }; + +=========================================================== +ACSVM::String +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/String.hpp> + class String : public StringData + { + public: + std::size_t lock; + + Word const idx; + Word const len0; + + bool ref; + + char get(std::size_t i) const; + }; + +=========================================================== +ACSVM::StringTable +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/String.hpp> + class StringTable + { + public: + StringTable(); + StringTable(StringTable &&table); + ~StringTable(); + + String &operator [] (Word idx) const; + String &operator [] (StringData const &data); + + void clear(); + + void collectBegin(); + void collectEnd(); + + String &getNone(); + + std::size_t size() const; + }; + +=============================================================================== +Threads <ACSVM/ACSVM/Thread.hpp> +=============================================================================== + +=========================================================== +ACSVM::ThreadState +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Thread.hpp> + class ThreadState + { + public: + enum State + { + Inactive, + Running, + Stopped, + Paused, + WaitScrI, + WaitScrS, + WaitTag, + }; + + + ThreadState(); + ThreadState(State state); + ThreadState(State state, Word data); + ThreadState(State state, Word data, Word type); + + bool operator == (State s) const; + bool operator != (State s) const; + + State state; + Word data; + Word type; + }; + +=========================================================== +ACSVM::ThreadInfo +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Thread.hpp> + class ThreadInfo + { + public: + virtual ~ThreadInfo() {} + }; + +=========================================================== +ACSVM::Thread +=========================================================== + +Synopsis: + #include <ACSVM/ACSVM/Thread.hpp> + class Thread + { + public: + virtual ThreadInfo const *getInfo() const; + + virtual void lockStrings() const; + + virtual void unlockStrings() const; + + Environment *const env; + + Stack<CallFrame> callStk; + Stack<Word> dataStk; + Store<Array> localArr; + Store<Word> localReg; + PrintBuf printBuf; + ThreadState state; + + Word const *codePtr; + Module *module; + GlobalScope *scopeGbl; + HubScope *scopeHub; + MapScope *scopeMap; + ModuleScope *scopeMod; + Script *script; + Word delay; + Word result; + }; + +############################################################################### +EOF +############################################################################### + diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 2dd78d8c0fa053c5526e5ad34dabe1e72b13f84d..5fc7bf5d60c87c7719eaffb9fac168ac0ee04479 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -22,7 +22,6 @@ include("cpm-discordrpc.cmake") include("cpm-xmp-lite.cmake") include("cpm-fmt.cmake") include("cpm-imgui.cmake") -include("cpm-acsvm.cmake") if (SRB2_CONFIG_ENABLE_WEBM_MOVIES) if (NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") diff --git a/thirdparty/cpm-acsvm.cmake b/thirdparty/cpm-acsvm.cmake deleted file mode 100644 index 2318050bbb9ab029bb0d513e0b207f7f4a2ab8b3..0000000000000000000000000000000000000000 --- a/thirdparty/cpm-acsvm.cmake +++ /dev/null @@ -1,77 +0,0 @@ -CPMAddPackage( - NAME acsvm - VERSION 0 - URL "https://github.com/DavidPH/ACSVM/archive/7011af443dd03e8592d7810b0b91f46c49bdde59.zip" - EXCLUDE_FROM_ALL ON - DOWNLOAD_ONLY YES -) - -if(acsvm_ADDED) - # Sal -- While ACSVM can be built as a shared library, a lot of its options are - # tied to directories existing, because the project suggests just copying it into - # your own project directly. I don't want us to do that, so I made our own target. - set( - acsvm_SOURCES - - ACSVM/Action.cpp - ACSVM/Action.hpp - ACSVM/Array.cpp - ACSVM/Array.hpp - ACSVM/BinaryIO.cpp - ACSVM/BinaryIO.hpp - ACSVM/CallFunc.cpp - ACSVM/CallFunc.hpp - ACSVM/Code.hpp - ACSVM/CodeData.cpp - ACSVM/CodeData.hpp - ACSVM/CodeList.hpp - ACSVM/Environment.cpp - ACSVM/Environment.hpp - ACSVM/Error.cpp - ACSVM/Error.hpp - ACSVM/Function.cpp - ACSVM/Function.hpp - ACSVM/HashMap.hpp - ACSVM/HashMapFixed.hpp - ACSVM/ID.hpp - ACSVM/Init.cpp - ACSVM/Init.hpp - ACSVM/Jump.cpp - ACSVM/Jump.hpp - ACSVM/Module.cpp - ACSVM/Module.hpp - ACSVM/ModuleACS0.cpp - ACSVM/ModuleACSE.cpp - ACSVM/PrintBuf.cpp - ACSVM/PrintBuf.hpp - ACSVM/Scope.cpp - ACSVM/Scope.hpp - ACSVM/Script.cpp - ACSVM/Script.hpp - ACSVM/Serial.cpp - ACSVM/Serial.hpp - ACSVM/Stack.hpp - ACSVM/Store.hpp - ACSVM/String.cpp - ACSVM/String.hpp - ACSVM/Thread.cpp - ACSVM/Thread.hpp - ACSVM/ThreadExec.cpp - ACSVM/Tracer.cpp - ACSVM/Tracer.hpp - ACSVM/Types.hpp - ACSVM/Vector.hpp - - Util/Floats.cpp - Util/Floats.hpp - ) - list(TRANSFORM acsvm_SOURCES PREPEND "${acsvm_SOURCE_DIR}/") - add_library(acsvm "${SRB2_INTERNAL_LIBRARY_TYPE}" ${acsvm_SOURCES}) - - target_compile_features(acsvm PRIVATE cxx_std_11) - - target_include_directories(acsvm INTERFACE "${acsvm_SOURCE_DIR}") - - target_link_libraries(acsvm PRIVATE acsvm::acsvm) - add_library(acsvm::acsvm ALIAS acsvm) -endif()