Initial commit
This commit is contained in:
391
engines/glk/advsys/vm.h
Normal file
391
engines/glk/advsys/vm.h
Normal file
@@ -0,0 +1,391 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ADVSYS_VM
|
||||
#define GLK_ADVSYS_VM
|
||||
|
||||
#include "glk/advsys/glk_interface.h"
|
||||
#include "glk/advsys/game.h"
|
||||
#include "common/stack.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace AdvSys {
|
||||
|
||||
/**
|
||||
* Execution result states
|
||||
*/
|
||||
enum ExecutionResult {
|
||||
IN_PROGRESS = 0, ///< Default state whilst script is in progress
|
||||
FINISH = 1, ///< Script was finished
|
||||
CHAIN = 2, ///< Another script is being chained to
|
||||
ABORT = 3 ///< Script was aborted
|
||||
};
|
||||
|
||||
/**
|
||||
* Opcode list
|
||||
*/
|
||||
enum Opcode {
|
||||
OP_BRT = 0x01, ///< Branch on true
|
||||
OP_BRF = 0x02, ///< Branch on false
|
||||
OP_BR = 0x03, ///< Branch unconditionally
|
||||
OP_T = 0x04, ///< Load top of stack with t
|
||||
OP_NIL = 0x05, ///< Load top of stack with nil
|
||||
OP_PUSH = 0x06, ///< Push nil onto stack
|
||||
OP_NOT = 0x07, ///< Logical negate top of stack
|
||||
OP_ADD = 0x08, ///< Add two numeric expressions
|
||||
OP_SUB = 0x09, ///< Subtract two numeric expressions
|
||||
OP_MUL = 0x0A, ///< Multiply two numeric expressions
|
||||
OP_DIV = 0x0B, ///< Divide two numeric expressions
|
||||
OP_REM = 0x0C, ///< Remainder of two numeric expressions
|
||||
OP_BAND = 0x0D, ///< Bitwise and of two numeric expressions
|
||||
OP_BOR = 0x0E, ///< Bitwise or of two numeric expressions
|
||||
OP_BNOT = 0x0F, ///< Bitwise not
|
||||
OP_LT = 0x10, ///< Less than
|
||||
OP_EQ = 0x11, ///< Equal to
|
||||
OP_GT = 0x12, ///< Greater than
|
||||
OP_LIT = 0x13, ///< Load literal
|
||||
OP_VAR = 0x14, ///< Load a variable value
|
||||
OP_GETP = 0x15, ///< Get the value of an object property
|
||||
OP_SETP = 0x16, ///< Set the value of an object property
|
||||
OP_SET = 0x17, ///< Set the value of a variable
|
||||
OP_PRINT = 0x18, ///< Print messages
|
||||
OP_TERPRI = 0x19, ///< Terminate the print line
|
||||
OP_PNUMBER = 0x1A, ///< Print a number
|
||||
OP_FINISH = 0x1B, ///< Finish handling this command
|
||||
OP_CHAIN = 0x1C, ///< Chain to the next handler
|
||||
OP_ABORT = 0x1D, ///< Abort this command
|
||||
OP_EXIT = 0x1E, ///< Exit the game
|
||||
OP_RETURN = 0x1F, ///< Return from function
|
||||
OP_CALL = 0x20, ///< Call subroutine
|
||||
OP_SVAR = 0x21, ///< Short load a variable
|
||||
OP_SSET = 0x22, ///< Short set a variable
|
||||
OP_SPLIT = 0x23, ///< Short load a positive literal
|
||||
OP_SNLIT = 0x24, ///< Short load a negative literal
|
||||
OP_YORN = 0x25, ///< Yes or No predicate
|
||||
OP_SAVE = 0x26, ///< Save data structures
|
||||
OP_RESTORE = 0x27, ///< Restore data structures
|
||||
OP_ARG = 0x28, ///< Load an argument value
|
||||
OP_ASET = 0x29, ///< Set an argument value
|
||||
OP_TMP = 0x2A, ///< Load a temporary variable value
|
||||
OP_TSET = 0x2B, ///< Set a temporary variable
|
||||
OP_TSPACE = 0x2C, ///< Allocate temporary variable space
|
||||
OP_CLASS = 0x2D, ///< Get the class of an object
|
||||
OP_MATCH = 0x2E, ///< Match a noun phrase with an object
|
||||
OP_PNOUN = 0x2F, ///< Print a noun phrase
|
||||
OP_RESTART = 0x30, ///< Restart the current game
|
||||
OP_RAND = 0x31, ///< Generate a random number
|
||||
OP_RNDMIZE = 0x32, ///< Seed the random number generator
|
||||
OP_SEND = 0x33, ///< Send a message to an object
|
||||
OP_VOWEL = 0x34, ///< Check for vowel beginning string
|
||||
|
||||
OP_XVAR = 0x40, ///< Extra short load a variable
|
||||
OP_XSET = 0x60, ///< Extra short set a variable
|
||||
OP_XPLIT = 0x80, ///< Extra short load a positive literal
|
||||
OP_XNLIT = 0xC0 ///< Extra short load a negative literal
|
||||
};
|
||||
|
||||
/**
|
||||
* Indexes useable in function pointer array offsets
|
||||
*/
|
||||
enum FPOffset {
|
||||
FP_FP = 0,
|
||||
FP_PC = 1,
|
||||
FP_ARGS_SIZE = 2,
|
||||
FP_ARGS = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Action flags
|
||||
*/
|
||||
enum ActionFlag {
|
||||
A_ACTOR = 1, ///< Actor
|
||||
A_DOBJECT = 2, ///< Direct object
|
||||
A_IOBJECT = 4 ///< Indirect object
|
||||
};
|
||||
|
||||
|
||||
class VM;
|
||||
typedef void (VM::*OpcodeMethod)();
|
||||
|
||||
/**
|
||||
* Fixed stack
|
||||
*/
|
||||
class FixedStack : public Common::FixedStack<int, 500> {
|
||||
public:
|
||||
/**
|
||||
* Resize the stack
|
||||
*/
|
||||
void resize(size_t newSize) {
|
||||
assert(newSize <= 500);
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate extra space on the stack
|
||||
*/
|
||||
void allocate(size_t amount) {
|
||||
uint oldSize = _size;
|
||||
resize(_size + amount);
|
||||
Common::fill(&_stack[oldSize], &_stack[oldSize + amount], 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a function pointer reference into the stack. It also allows
|
||||
* positive array indexing to reference the following:
|
||||
* 0 = Previous function pointer
|
||||
* 1 = Return PC
|
||||
* 2 = Size of argument block
|
||||
* 3+ = Any function call arguments
|
||||
*/
|
||||
class FunctionPointer {
|
||||
private:
|
||||
FixedStack &_stack;
|
||||
int _index;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FunctionPointer(FixedStack &s) : _stack(s), _index(0) {}
|
||||
|
||||
/**
|
||||
* Array indexing
|
||||
*/
|
||||
int &operator[](int idx) { return _stack[_index - idx - 1]; }
|
||||
|
||||
/**
|
||||
* Sets the index in the stack of the function pointer
|
||||
*/
|
||||
FunctionPointer &operator=(int index) {
|
||||
_index = index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the function pointer
|
||||
*/
|
||||
void clear() { _index = 0; }
|
||||
|
||||
/**
|
||||
* Returns the index in the stack of the function pointer
|
||||
*/
|
||||
operator int() const { return _index; }
|
||||
|
||||
/**
|
||||
* Sets the function pointer to the top of the stack
|
||||
*/
|
||||
void set() {
|
||||
_index = _stack.size();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main VM for AdvSys
|
||||
*/
|
||||
class VM : public GlkInterface, public Game {
|
||||
struct InputWord {
|
||||
Common::String _text;
|
||||
int _number;
|
||||
|
||||
InputWord() : _number(0) {}
|
||||
operator int() const { return _number; }
|
||||
};
|
||||
struct AdjectiveEntry {
|
||||
int _list;
|
||||
int _word;
|
||||
|
||||
AdjectiveEntry() : _list(0), _word(0) {}
|
||||
};
|
||||
struct Noun {
|
||||
int _noun;
|
||||
int _num;
|
||||
AdjectiveEntry *_adjective;
|
||||
|
||||
Noun() : _noun(0), _num(0), _adjective(nullptr) {}
|
||||
};
|
||||
private:
|
||||
// Execution fields
|
||||
static OpcodeMethod _METHODS[0x34];
|
||||
int _pc;
|
||||
ExecutionResult _status;
|
||||
FixedStack _stack;
|
||||
FunctionPointer _fp;
|
||||
|
||||
// Parser fields
|
||||
int _actor;
|
||||
int _action;
|
||||
int _dObject;
|
||||
int _ndObjects;
|
||||
int _iObject;
|
||||
Common::Array<InputWord> _words;
|
||||
Common::Array<InputWord>::iterator _wordPtr;
|
||||
Common::Array<int> _verbs;
|
||||
Common::Array<AdjectiveEntry> _adjectiveList;
|
||||
Common::Array<Noun> _nouns;
|
||||
private:
|
||||
/**
|
||||
* Execute a single opcode within the script
|
||||
*/
|
||||
void executeOpcode();
|
||||
|
||||
/**
|
||||
* Get the next code byte and increments the PC counter
|
||||
*/
|
||||
int readCodeByte() {
|
||||
return getCodeByte(_pc++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next code word and increases the PC counter to after it
|
||||
*/
|
||||
int readCodeWord() {
|
||||
int v = getCodeWord(_pc);
|
||||
_pc += 2;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an input line and parse it
|
||||
*/
|
||||
bool parseInput();
|
||||
|
||||
/**
|
||||
* Gets an input line and splits it up into the words array
|
||||
*/
|
||||
bool getLine();
|
||||
|
||||
/**
|
||||
* Get the next word of a passed input line
|
||||
* @param line Input line
|
||||
* @returns True if a valid word was extracted
|
||||
*/
|
||||
bool getWord(Common::String &line);
|
||||
|
||||
/**
|
||||
* Get a noun phrase and return the object it refers to
|
||||
*/
|
||||
uint getNoun();
|
||||
|
||||
/**
|
||||
* Get a verb phrase and return the action it refers to
|
||||
*/
|
||||
bool getVerb();
|
||||
|
||||
/**
|
||||
* Match an object against a name and list of adjectives
|
||||
*/
|
||||
bool match(int obj, int noun, const AdjectiveEntry *adjectives);
|
||||
|
||||
/**
|
||||
* Called when a parsing error occurs
|
||||
*/
|
||||
void parseError();
|
||||
|
||||
/**
|
||||
* Returns true if a passed character is a skippable whitespace
|
||||
*/
|
||||
static bool isWhitespace(char c);
|
||||
|
||||
/**
|
||||
* Skips over spaces in a passed string
|
||||
*/
|
||||
static bool skipSpaces(Common::String &str);
|
||||
private:
|
||||
void opBRT();
|
||||
void opBRF();
|
||||
void opBR();
|
||||
void opT();
|
||||
void opNIL();
|
||||
void opPUSH();
|
||||
void opNOT();
|
||||
void opADD();
|
||||
void opSUB();
|
||||
void opMUL();
|
||||
void opDIV();
|
||||
void opREM();
|
||||
void opBAND();
|
||||
void opBOR();
|
||||
void opBNOT();
|
||||
void opLT();
|
||||
void opEQ();
|
||||
void opGT();
|
||||
void opLIT();
|
||||
void opVAR();
|
||||
void opGETP();
|
||||
void opSETP();
|
||||
void opSET();
|
||||
void opPRINT();
|
||||
void opTERPRI();
|
||||
void opPNUMBER();
|
||||
void opFINISH();
|
||||
void opCHAIN();
|
||||
void opABORT();
|
||||
void opEXIT();
|
||||
void opRETURN();
|
||||
void opCALL();
|
||||
void opSVAR();
|
||||
void opSSET();
|
||||
void opSPLIT();
|
||||
void opSNLIT();
|
||||
void opYORN();
|
||||
void opSAVE();
|
||||
void opRESTORE();
|
||||
void opARG();
|
||||
void opASET();
|
||||
void opTMP();
|
||||
void opTSET();
|
||||
void opTSPACE();
|
||||
void opCLASS();
|
||||
void opMATCH();
|
||||
void opPNOUN();
|
||||
void opRESTART();
|
||||
void opRAND();
|
||||
void opRNDMIZE();
|
||||
void opSEND();
|
||||
void opVOWEL();
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
VM(OSystem *syst, const GlkGameDescription &gameDesc);
|
||||
|
||||
/**
|
||||
* Exeecute a script
|
||||
* @param offset Script offset
|
||||
* @returns Script result code
|
||||
*/
|
||||
ExecutionResult execute(int offset);
|
||||
|
||||
/**
|
||||
* Get an input line and parse it
|
||||
*/
|
||||
bool getInput();
|
||||
|
||||
/**
|
||||
* Get the next command (next direct object)
|
||||
*/
|
||||
bool nextCommand();
|
||||
};
|
||||
|
||||
} // End of namespace AdvSys
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user