-
Notifications
You must be signed in to change notification settings - Fork 1
ACS VM library and standalone interpreter.
License
DavidPH/ACSVM
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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.
About
ACS VM library and standalone interpreter.
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published