1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-23 02:42:39 +01:00

Refactor script system to bind named functions.

Replace ugly macros with named functions and binding to script modules
This commit is contained in:
Daniel Evans 2015-01-16 02:32:31 +00:00
parent 698f5a9d91
commit 39ff9df337
13 changed files with 1978 additions and 1961 deletions

View File

@ -1,12 +0,0 @@
#pragma once
#ifndef _OPCODES_3_HPP_
#define _OPCODES_3_HPP_
#include <script/ScriptTypes.hpp>
struct Opcodes3 : SCMOpcodes
{
Opcodes3();
};
#endif

View File

@ -1,19 +0,0 @@
#pragma once
#ifndef _OPCODESVM_HPP_
#define _OPCODESVM_HPP_
#include <script/ScriptTypes.hpp>
namespace Opcodes {
struct VM {
static VM& get() {
static VM vm;
return vm;
}
VM();
SCMMicrocodeTable codes;
};
}
#endif

View File

@ -0,0 +1,44 @@
#ifndef _SCRIPTMODULE_HPP_
#define _SCRIPTMODULE_HPP_
#include <script/ScriptTypes.hpp>
#include "ScriptMachine.hpp"
#include <string>
/**
* Interface for a collection of functions that can be exported to a game script interface.
*
* For example a collection of functions that control the time of day, or create objects would
* be the collected within one ScriptModule with a sensible name like "Environment" or "Objects"
*/
class ScriptModule
{
public:
ScriptModule(const std::string& name) : name(name) { }
const std::string& getName() const { return name; }
void bind(ScriptFunctionID id,
ScriptFunction func,
int args,
const std::string& name,
const std::string& desc
);
bool findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out);
private:
const std::string name;
std::map<ScriptFunctionID, ScriptFunctionMeta> functions;
};
template<class Tret> ScriptFunction conditional_facade(Tret(*f)(const ScriptArguments&)) { return f; }
template<> ScriptFunction conditional_facade<bool>(bool(*f)(const ScriptArguments&));
// Macro to automatically use function name.
#define bindFunction(id, func, argc, desc) \
bind(id, conditional_facade(func), argc, #func, desc)
#define bindUnimplemented(id, func, argc, desc) \
bind(id, 0, argc, #func, desc)
#endif

View File

@ -8,6 +8,7 @@
#include <functional>
class ScriptMachine;
class ScriptModule;
struct SCMThread;
typedef uint16_t SCMOpcode;
@ -56,6 +57,41 @@ struct SCMOpcodeParameter {
typedef std::vector<SCMOpcodeParameter> SCMParams;
class ScriptArguments
{
const SCMParams* parameters;
SCMThread* thread;
ScriptMachine* machine;
public:
ScriptArguments(const SCMParams* p, SCMThread* t, ScriptMachine* m)
: parameters(p), thread(t), machine(m) { }
const SCMParams& getParameters() const { return *parameters; }
SCMThread* getThread() const { return thread; }
ScriptMachine* getVM() const { return machine; }
const SCMOpcodeParameter& operator[](unsigned int arg) const
{
return parameters->at(arg);
}
};
typedef std::function<void (const ScriptArguments&)> ScriptFunction;
typedef std::function<bool (const ScriptArguments&)> ScriptFunctionBoolean;
typedef uint16_t ScriptFunctionID;
struct ScriptFunctionMeta
{
ScriptFunction function;
int arguments;
bool conditional;
/** API name for this function */
const std::string signature;
/** Human friendly description */
const std::string description;
};
struct SCMMicrocode {
std::string name;
int parameters;
@ -66,20 +102,12 @@ typedef std::map<SCMOpcode, SCMMicrocode> SCMMicrocodeTable;
struct SCMOpcodes
{
typedef std::function<void (ScriptMachine*, SCMThread*, SCMParams*)> SCMFunc;
std::map<SCMOpcode, SCMMicrocode> codes;
std::vector<ScriptModule*> modules;
~SCMOpcodes();
bool findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out);
};
#define OPC_UNIMPLEMENTED_MSG(code, name) [=](ScriptMachine* m, SCMThread* t, SCMParams* p) { std::cout << #code << " " << name << " unimplemented" << std::endl; }
#define VM_OPCODE_DEF(code) void _opcode_##code##_func(ScriptMachine* m, SCMThread* t, SCMParams* p)
#define VM_CONDOPCODE_DEF(code) bool _opcode_##code##_func(ScriptMachine* m, SCMThread* t, SCMParams* p)
#define VM_OPCODE_DEC(code, parameters, name) codes[code] = SCMMicrocode{name, parameters, SCMOpcodes::SCMFunc(_opcode_##code##_func)}
#define VM_CONDOPCODE_DEC(code, parameters, cname) codes[code] = SCMMicrocode{cname, parameters, \
[=](ScriptMachine* m, SCMThread* t, SCMParams* p) { t->conditionResult = _opcode_##code##_func(m, t, p); }}
#define VM_OPCODE_DEC_U(code, parameters, name) codes.insert({code, {name, parameters, {}}})
#endif

View File

@ -0,0 +1,12 @@
#ifndef _MODULE_GAME_HPP_
#define _MODULE_GAME_HPP_
#include <script/ScriptModule.hpp>
class GameModule : public ScriptModule
{
public:
GameModule();
};
#endif

View File

@ -0,0 +1,11 @@
#ifndef _VM_MODULE_HPP_
#define _VM_MODULE_HPP_
#include <script/ScriptModule.hpp>
class VMModule : public ScriptModule
{
public:
VMModule();
};
#endif

View File

@ -7,8 +7,9 @@
#include <data/WeaponData.hpp>
#include <WorkContext.hpp>
#include <script/Opcodes3.hpp>
#include <script/ScriptMachine.hpp>
#include <script/modules/VMModule.hpp>
#include <script/modules/GameModule.hpp>
// 3 isn't enough to cause a factory.
#include <objects/CharacterObject.hpp>
@ -172,8 +173,12 @@ void GameWorld::runScript(const std::string &name)
SCMFile* f = gameData.loadSCM(name);
if( f ) {
if( script ) delete script;
SCMOpcodes* opcodes = new SCMOpcodes;
opcodes->modules.push_back(new VMModule);
opcodes->modules.push_back(new GameModule);
script = new ScriptMachine(this, f, new Opcodes3);
script = new ScriptMachine(this, f, opcodes);
}
else {
logError("Failed to load SCM: " + name);

File diff suppressed because it is too large Load Diff

View File

@ -1,276 +0,0 @@
#include <script/OpcodesVM.hpp>
#include <script/ScriptMachine.hpp>
#include <script/SCMFile.hpp>
#include <engine/GameWorld.hpp>
#include <iostream>
SCMThread::pc_t localizeLabel(SCMThread* t, int label)
{
// Negative jump addresses indicate a jump relative to the start of the thread.
return (label < 0) ? (t->baseAddress + (-label)) : label;
}
VM_OPCODE_DEF( 0x0001 )
{
t->wakeCounter = p->at(0).integer;
if( t->wakeCounter == 0 ) {
t->wakeCounter = -1;
}
}
VM_OPCODE_DEF( 0x0002 )
{
t->programCounter = localizeLabel(t, p->at(0).integer);
}
VM_OPCODE_DEF( 0x0004 )
{
*p->at(0).globalInteger = p->at(1).integer;
}
VM_OPCODE_DEF( 0x0005 )
{
*p->at(0).globalReal = p->at(1).real;
}
VM_OPCODE_DEF( 0x0006 )
{
*p->at(0).globalInteger = p->at(1).integer;
}
VM_OPCODE_DEF( 0x0008 )
{
*p->at(0).globalInteger += p->at(1).integer;
}
VM_OPCODE_DEF( 0x0009 )
{
*p->at(0).globalReal += p->at(1).real;
}
VM_OPCODE_DEF( 0x000C )
{
*p->at(0).globalInteger -= p->at(1).integer;
}
VM_OPCODE_DEF( 0x000D )
{
*p->at(0).globalReal -= p->at(1).real;
}
VM_OPCODE_DEF( 0x0015 )
{
*p->at(0).globalReal /= p->at(1).real;
}
VM_OPCODE_DEF( 0x0018 ) {
t->conditionResult = *p->at(0).globalInteger > p->at(1).integer;
}
VM_OPCODE_DEF( 0x0019 ) {
t->conditionResult = *p->at(0).globalInteger > p->at(1).integer;
}
VM_OPCODE_DEF( 0x001A ) {
t->conditionResult = p->at(0).integer > *p->at(1).globalInteger;
}
VM_OPCODE_DEF( 0x001B ) {
t->conditionResult = p->at(0).integer > *p->at(1).globalInteger;
}
VM_OPCODE_DEF( 0x0020 ) {
t->conditionResult = *p->at(0).globalReal > p->at(1).real;
}
VM_OPCODE_DEF( 0x0028 ) {
t->conditionResult = *p->at(0).globalInteger >= p->at(1).integer;
}
VM_OPCODE_DEF( 0x0029 )
{
t->conditionResult = *p->at(0).globalInteger >= p->at(1).integer;
}
VM_OPCODE_DEF( 0x002A ) {
t->conditionResult = p->at(0).integer >= *p->at(1).globalInteger;
}
VM_OPCODE_DEF( 0x0038 ) {
t->conditionResult = *p->at(0).globalInteger == p->at(1).integer;
}
VM_OPCODE_DEF( 0x0039 ) {
t->conditionResult = *p->at(0).globalInteger == p->at(1).integer;
}
VM_OPCODE_DEF( 0x004F )
{
std::cout << t->name << " spawning thread at " << p->at(0).integer << std::endl;
m->startThread(p->at(0).integer);
}
VM_OPCODE_DEF( 0x004D )
{
if( ! t->conditionResult ) {
t->programCounter = localizeLabel(t, p->at(0).integer);
}
}
VM_OPCODE_DEF( 0x004E )
{
// ensure the thread is immediately yeilded
t->wakeCounter = -1;
t->finished = true;
}
VM_OPCODE_DEF( 0x0050 )
{
t->calls.push(t->programCounter);
t->programCounter = localizeLabel(t, p->at(0).integer);
}
VM_OPCODE_DEF( 0x0051 )
{
t->programCounter = t->calls.top();
t->calls.pop();
}
VM_OPCODE_DEF( 0x0060 )
{
*p->at(0).globalInteger -= *p->at(1).globalInteger;
}
VM_OPCODE_DEF( 0x0061 )
{
*p->at(0).globalReal -= *p->at(1).globalReal;
}
VM_OPCODE_DEF( 0x0084 )
{
*p->at(0).globalInteger = *p->at(1).globalInteger;
}
VM_OPCODE_DEF( 0x0086 )
{
*p->at(0).globalReal = *p->at(1).globalReal;
}
VM_OPCODE_DEF( 0x00D6 )
{
auto n = p->at(0).integer;
if( n <= 7 ) {
t->conditionCount = n+1;
t->conditionMask = 0xFF;
t->conditionAND = true;
}
else {
t->conditionCount = n-19;
t->conditionMask = 0x00;
t->conditionAND = false;
}
}
VM_OPCODE_DEF( 0x00D7 )
{
std::cout << "Starting Mission Thread at " << p->at(0).integer << std::endl;
m->startThread(p->at(0).integer, true);
}
VM_OPCODE_DEF( 0x00D8 )
{
std::cout << "Ended: " << t->name << std::endl;
for( auto& o : m->getWorld()->state.missionObjects )
{
m->getWorld()->destroyObjectQueued(o);
}
m->getWorld()->state.missionObjects.clear();
*m->getWorld()->state.scriptOnMissionFlag = 0;
}
VM_OPCODE_DEF( 0x02CD )
{
t->calls.push(t->programCounter);
t->programCounter = p->at(0).integer;
}
VM_OPCODE_DEF( 0x03A4 )
{
t->name = p->at(0).string;
std::cout << "Thread renamed: " << t->name << std::endl;
}
VM_OPCODE_DEF( 0x0417 )
{
std::cout << "Starting mission no. " << p->at(0).integer << std::endl;
auto offset = m->getFile()->getMissionOffsets()[p->at(0).integer];
m->startThread(offset, true);
}
namespace Opcodes {
VM::VM()
{
VM_OPCODE_DEC( 0x0001, 1, "Wait" );
VM_OPCODE_DEC( 0x0002, 1, "Jump" );
VM_OPCODE_DEC( 0x0004, 2, "Set Global Integer" );
VM_OPCODE_DEC( 0x0005, 2, "Set Global Float" );
VM_OPCODE_DEC( 0x0006, 2, "Set Local Int" );
VM_OPCODE_DEC( 0x0008, 2, "Increment Global Int" );
VM_OPCODE_DEC( 0x0009, 2, "Increment Global Float" );
VM_OPCODE_DEC( 0x000C, 2, "Decrement Global Int" );
VM_OPCODE_DEC( 0x000D, 2, "Decrement Global Float" );
VM_OPCODE_DEC( 0x0015, 2, "Divide Global by Float" );
VM_OPCODE_DEC( 0x0018, 2, "Global Int Greater than Int" );
VM_OPCODE_DEC( 0x0019, 2, "Local Int Greater than Int" );
VM_OPCODE_DEC( 0x001A, 2, "Int Greater Than Global Int" );
VM_OPCODE_DEC( 0x001B, 2, "Int Greater Than Var Int" );
VM_OPCODE_DEC( 0x0020, 2, "Global Float Greather than Float" );
VM_OPCODE_DEC( 0x0028, 2, "Global Int >= Int" );
VM_OPCODE_DEC( 0x0029, 2, "Local Int >= Int" );
VM_OPCODE_DEC( 0x002A, 2, "Int >= Global Int" );
VM_OPCODE_DEC( 0x0038, 2, "Global Int Equal to Int" );
VM_OPCODE_DEC( 0x0039, 2, "Local Int Equal to Int" );
VM_OPCODE_DEC( 0x004F, -1, "Start New Thread" );
VM_OPCODE_DEC( 0x004D, 1, "Jump if false" );
VM_OPCODE_DEC( 0x004E, 0, "End Thread" );
VM_OPCODE_DEC( 0x0050, 1, "Gosub" );
VM_OPCODE_DEC( 0x0051, 0, "Return" );
VM_OPCODE_DEC( 0x0060, 2, "Decrement Global Integer by Global Integer" );
VM_OPCODE_DEC( 0x0061, 2, "Decrement Global Float by Global Float" );
VM_OPCODE_DEC( 0x0084, 2, "Set Global Int To Global" );
VM_OPCODE_DEC( 0x0086, 2, "Set Global Float To Global" );
VM_OPCODE_DEC( 0x00D6, 1, "If" );
VM_OPCODE_DEC( 0x00D7, 1, "Start Mission Thread" );
VM_OPCODE_DEC( 0x00D8, 0, "Set Mission Finished" );
VM_OPCODE_DEC( 0x02CD, 2, "Call" );
VM_OPCODE_DEC( 0x03A4, 1, "Name Thread" );
VM_OPCODE_DEC( 0x0417, 1, "Start Mission" );
}
}

View File

@ -1,11 +1,32 @@
#include <script/ScriptMachine.hpp>
#include <script/SCMFile.hpp>
#include <boost/iterator/iterator_concepts.hpp>
#include <script/ScriptModule.hpp>
#if SCM_DEBUG_INSTRUCTIONS
#include <iostream>
#endif
SCMOpcodes::~SCMOpcodes()
{
for(auto m : modules)
{
delete m;
}
}
bool SCMOpcodes::findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out)
{
for(ScriptModule* module : modules)
{
if( module->findOpcode(id, out) )
{
return true;
}
}
return false;
}
void ScriptMachine::executeThread(SCMThread &t, int msPassed)
{
if( t.wakeCounter > 0 ) {
@ -20,16 +41,19 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
bool isNegatedConditional = ((opcode & SCM_NEGATE_CONDITIONAL_MASK) == SCM_NEGATE_CONDITIONAL_MASK);
opcode = opcode & ~SCM_NEGATE_CONDITIONAL_MASK;
auto it = _ops->codes.find(opcode);
if( it == _ops->codes.end() ) throw IllegalInstruction(opcode, t.programCounter, t.name);
ScriptFunctionMeta* foundcode;
if( ! _ops->findOpcode(opcode, &foundcode) )
{
throw IllegalInstruction(opcode, t.programCounter, t.name);
}
auto& code = *foundcode;
t.programCounter += sizeof(SCMOpcode);
SCMMicrocode& code = it->second;
SCMParams parameters;
bool hasExtraParameters = code.parameters < 0;
auto requiredParams = std::abs(code.parameters);
bool hasExtraParameters = code.arguments < 0;
auto requiredParams = std::abs(code.arguments);
for( int p = 0; p < requiredParams || hasExtraParameters; ++p ) {
auto type_r = _file->read<SCMByte>(t.programCounter);
@ -87,7 +111,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
};
}
if(! code.func)
if(! code.function)
{
#if SCM_DEBUG_INSTRUCTIONS
std::cout << std::setw(7) << std::setfill(' ') << t.name <<
@ -115,7 +139,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
}
std::cout << " ";
}
std::cout << code.name << " unimplemented"<< std::endl;
std::cout << code.signature << " unimplemented"<< std::endl;
#endif
}
else
@ -153,10 +177,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
}
std::cout << " ";
}
std::cout << code.name << std::endl;
std::cout << code.signature << std::endl;
}
#endif
code.func(this, &t, &parameters);
ScriptArguments sca(&parameters, &t, this);
code.function(sca);
}
if(isNegatedConditional) {
@ -193,14 +218,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
p.globalPtr = (t.locals + 17 * sizeof ( SCMByte ) * 4);
*p.globalInteger += msPassed;
if( t.wakeCounter == -1 ) {
t.wakeCounter = 0;
}
}
#include <iostream>
ScriptMachine::ScriptMachine(GameWorld *world, SCMFile *file, SCMOpcodes *ops)
: _file(file), _ops(ops), _world(world)
{
@ -211,7 +233,6 @@ ScriptMachine::ScriptMachine(GameWorld *world, SCMFile *file, SCMOpcodes *ops)
{
_globals[i] = 0;
}
std::cout << globals << " " << SCM_VARIABLE_SIZE << std::endl;
}
ScriptMachine::~ScriptMachine()

View File

@ -0,0 +1,31 @@
#include <script/ScriptModule.hpp>
#include <script/ScriptMachine.hpp>
void ScriptModule::bind(ScriptFunctionID id, ScriptFunction func, int args, const std::string& name, const std::string& desc)
{
functions.insert(
{ id,
{
func,
args,
false,
name,
desc
}
}
);
}
bool ScriptModule::findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out)
{
auto it = functions.find(id);
if( it == functions.end() )
{
return false;
}
*out = &functions[id];
return true;
}
template<> ScriptFunction conditional_facade<bool>(bool(*f)(const ScriptArguments&))
{ return [=](const ScriptArguments& a) { return a.getThread()->conditionResult = f(a); }; }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,245 @@
#include <script/modules/VMModule.hpp>
#include <script/ScriptMachine.hpp>
#include <script/SCMFile.hpp>
#include <engine/GameWorld.hpp>
SCMThread::pc_t localizeLabel(SCMThread* t, int label)
{
// Negative jump addresses indicate a jump relative to the start of the thread.
return (label < 0) ? (t->baseAddress + (-label)) : label;
}
void vm_sleep(const ScriptArguments& args)
{
args.getThread()->wakeCounter = args[0].integer;
if( args.getThread()->wakeCounter == 0 ) {
args.getThread()->wakeCounter = -1;
}
}
void vm_jump(const ScriptArguments& args)
{
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
}
void vm_global_int(const ScriptArguments& args)
{
*args[0].globalInteger = args[1].integer;
}
void vm_global_float(const ScriptArguments& args)
{
*args[0].globalReal = args[1].real;
}
void vm_inc_global_int(const ScriptArguments& args)
{
*args[0].globalInteger += args[1].integer;
}
void vm_inc_global_float(const ScriptArguments& args)
{
*args[0].globalReal += args[1].real;
}
void vm_dec_global_int(const ScriptArguments& args)
{
*args[0].globalInteger -= args[1].integer;
}
void vm_dec_global_float(const ScriptArguments& args)
{
*args[0].globalReal -= args[1].real;
}
void vm_div_global_float(const ScriptArguments& args)
{
*args[0].globalReal /= args[1].real;
}
void vm_global_int_gt_int(const ScriptArguments& args)
{
args.getThread()->conditionResult = *args[0].globalInteger > args[1].integer;
}
void vm_int_gt_global_int(const ScriptArguments& args)
{
args.getThread()->conditionResult = args[0].integer > *args[1].globalInteger;
}
void vm_global_float_gt_float(const ScriptArguments& args)
{
args.getThread()->conditionResult = *args[0].globalReal > args[1].real;
}
void vm_global_int_ge_int(const ScriptArguments& args)
{
args.getThread()->conditionResult = *args[0].globalInteger >= args[1].integer;
}
void vm_int_ge_global_int(const ScriptArguments& args)
{
args.getThread()->conditionResult = args[0].integer >= *args[1].globalInteger;
}
void vm_global_int_eq_int(const ScriptArguments& args)
{
args.getThread()->conditionResult = *args[0].globalInteger == args[1].integer;
}
void vm_new_thread(const ScriptArguments& args)
{
args.getVM()->startThread(args[0].integer);
}
void vm_jump_if_false(const ScriptArguments& args)
{
if( ! args.getThread()->conditionResult ) {
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
}
}
void vm_halt_thread(const ScriptArguments& args)
{
// ensure the thread is immediately yeilded
args.getThread()->wakeCounter = -1;
args.getThread()->finished = true;
}
void vm_call(const ScriptArguments& args)
{
args.getThread()->calls.push(args.getThread()->programCounter);
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
}
void vm_return(const ScriptArguments& args)
{
args.getThread()->programCounter = args.getThread()->calls.top();
args.getThread()->calls.pop();
}
void vm_dec_global_int_by_global(const ScriptArguments& args)
{
*args[0].globalInteger -= *args[1].globalInteger;
}
void vm_dec_global_float_by_global(const ScriptArguments& args)
{
*args[0].globalReal -= *args[1].globalReal;
}
void vm_global_int_to_global(const ScriptArguments& args)
{
*args[0].globalInteger = *args[1].globalInteger;
}
void vm_global_float_to_global(const ScriptArguments& args)
{
*args[0].globalReal = *args[1].globalReal;
}
void vm_if(const ScriptArguments& args)
{
auto n = args[0].integer;
if( n <= 7 ) {
args.getThread()->conditionCount = n+1;
args.getThread()->conditionMask = 0xFF;
args.getThread()->conditionAND = true;
}
else {
args.getThread()->conditionCount = n-19;
args.getThread()->conditionMask = 0x00;
args.getThread()->conditionAND = false;
}
}
void vm_new_mission_thread(const ScriptArguments& args)
{
args.getVM()->startThread(args[0].integer, true);
}
void vm_mission_over(const ScriptArguments& args)
{
for( auto& o : args.getVM()->getWorld()->state.missionObjects )
{
args.getVM()->getWorld()->destroyObjectQueued(o);
}
args.getVM()->getWorld()->state.missionObjects.clear();
*args.getVM()->getWorld()->state.scriptOnMissionFlag = 0;
}
void vm_name_thread(const ScriptArguments& args)
{
args.getThread()->name = args[0].string;
}
void vm_start_mission(const ScriptArguments& args)
{
auto offset = args.getVM()->getFile()->getMissionOffsets()[args[0].integer];
args.getVM()->startThread(offset, true);
}
VMModule::VMModule()
: ScriptModule("VM")
{
bindFunction(0x001, vm_sleep, 1, "Sleep thread");
bindFunction(0x002, vm_jump, 1, "Jump");
bindFunction(0x004, vm_global_int, 2, "Set Global Integer");
bindFunction(0x005, vm_global_float, 2, "Set Global Float");
// Local Integers are handled by the VM itself, function can be re-used.
bindFunction(0x006, vm_global_int, 2, "Set Local Int");
bindFunction(0x008, vm_inc_global_int, 2, "Increment Global Int");
bindFunction(0x009, vm_inc_global_float, 2, "Increment Global Float");
bindFunction(0x00C, vm_dec_global_int, 2, "Decrement Global Int");
bindFunction(0x00D, vm_dec_global_float, 2, "Decrement Global Float");
bindFunction(0x015, vm_div_global_float, 2, "Divide Global by Float");
bindFunction(0x018, vm_global_int_gt_int, 2, "Global Int Greater than Int");
bindFunction(0x019, vm_global_int_gt_int, 2, "Local Int Greater than Int");
bindFunction(0x01A, vm_int_gt_global_int, 2, "Int Greater Than Global Int");
bindFunction(0x01B, vm_int_gt_global_int, 2, "Int Greater Than Var Int");
bindFunction(0x020, vm_global_float_gt_float, 2, "Global Float Greather than Float");
bindFunction(0x028, vm_global_int_ge_int, 2, "Global Int >= Int");
bindFunction(0x029, vm_global_int_ge_int, 2, "Local Int >= Int");
bindFunction(0x02A, vm_int_ge_global_int, 2, "Int >= Global Int");
bindFunction(0x038, vm_global_int_eq_int, 2, "Global Int Equal to Int");
bindFunction(0x039, vm_global_int_eq_int, 2, "Local Int Equal to Int");
bindFunction(0x04F, vm_new_thread, -1, "Start New Thread");
bindFunction(0x04D, vm_jump_if_false, 1, "Jump if false");
bindFunction(0x04E, vm_halt_thread, 0, "End Thread");
bindFunction(0x050, vm_call, 1, "Gosub");
bindFunction(0x051, vm_return, 0, "Return");
bindFunction(0x060, vm_dec_global_int_by_global, 2, "Decrement Global Integer by Global Integer");
bindFunction(0x061, vm_dec_global_float_by_global, 2, "Decrement Global Float by Global Float");
bindFunction(0x084, vm_global_int_to_global, 2, "Set Global Int To Global");
bindFunction(0x086, vm_global_float_to_global, 2, "Set Global Float To Global");
bindFunction(0x0D6, vm_if, 1, "If");
bindFunction(0x0D7, vm_new_mission_thread, 1, "Start Mission Thread");
bindFunction(0x0D8, vm_mission_over, 0, "Set Mission Finished");
bindFunction(0x2CD, vm_call, 2, "Call");
bindFunction(0x3A4, vm_name_thread, 1, "Name Thread");
bindFunction(0x417, vm_start_mission, 1, "Start Mission");
}