Files

253 lines
6.7 KiB
C++

/* 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 GAMOS_VM_H
#define GAMOS_VM_H
#include "common/array.h"
#include "common/hashmap.h"
namespace Gamos {
class VM {
public:
static constexpr const uint THREADS_COUNT = 2;
static constexpr const uint STACK_SIZE = 0x100;
static constexpr const uint STACK_POS = 0x80;
static constexpr const uint MEMTYPE_SHIFT = 30;
static constexpr const uint ADDRESS_MASK = (1 << MEMTYPE_SHIFT) - 1;
enum OP {
OP_EXIT = 0,
OP_CMP_EQ = 1,
OP_CMP_NE = 2,
OP_CMP_LE = 3,
OP_CMP_LEQ = 4,
OP_CMP_GR = 5,
OP_CMP_GREQ = 6,
OP_CMP_NAE = 7,
OP_CMP_NA = 8,
OP_CMP_A = 9,
OP_CMP_AE = 10,
OP_BRANCH = 11,
OP_JMP = 12,
OP_SP_ADD = 13,
OP_MOV_EDI_ECX_AL = 14,
OP_MOV_EBX_ECX_AL = 15,
OP_MOV_EDI_ECX_EAX = 16,
OP_MOV_EBX_ECX_EAX = 17,
OP_RET = 18,
OP_RETX = 19,
OP_MOV_EDX_EAX = 20,
OP_ADD_EAX_EDX = 21,
OP_MUL = 22,
OP_OR = 23,
OP_XOR = 24,
OP_AND = 25,
OP_NEG = 26,
OP_SAR = 27,
OP_SHL = 28,
OP_LOAD = 29,
OP_INC = 30,
OP_DEC = 31,
OP_XCHG = 32,
OP_PUSH_EAX = 33,
OP_POP_EDX = 34,
OP_LOAD_OFFSET_EDI = 35,
OP_LOAD_OFFSET_EDI2 = 36,
OP_LOAD_OFFSET_EBX = 37,
OP_LOAD_OFFSET_ESP = 38,
OP_MOV_PTR_EDX_AL = 39,
OP_MOV_PTR_EDX_EAX = 40,
OP_SHL_2 = 41,
OP_ADD_4 = 42,
OP_SUB_4 = 43,
OP_XCHG_ESP = 44,
OP_NEG_ADD = 45,
OP_DIV = 46,
OP_MOV_EAX_BPTR_EDI = 47,
OP_MOV_EAX_BPTR_EBX = 48,
OP_MOV_EAX_DPTR_EDI = 49,
OP_MOV_EAX_DPTR_EBX = 50,
OP_MOV_EAX_BPTR_EAX = 51,
OP_MOV_EAX_DPTR_EAX = 52,
OP_PUSH_ESI_ADD_EDI = 53,
OP_CALL_FUNC = 54,
OP_PUSH_ESI_SET_EDX_EDI = 55,
OP_MAX
};
enum MEMREF {
REF_UNK = 0,
REF_STACK = 1,
REF_EBX = 2,
REF_EDI = 3
};
struct ValAddr {
uint32 value = 0;
inline uint32 getVal() const { return value; };
inline void setVal(uint32 v) { value = v; };
inline uint32 getOffset() const { return value & ADDRESS_MASK; };
inline uint getMemType() const { return (value >> MEMTYPE_SHIFT ) & 3; };
inline void setMemType(uint tp) { value = (value & ADDRESS_MASK) | ((tp & 3) << MEMTYPE_SHIFT); };
inline void setOffset(uint32 offset) { value = (value & (~ADDRESS_MASK)) | (offset & ADDRESS_MASK); };
inline void setAddress(uint tp, uint32 offset) { value = (offset & ADDRESS_MASK) | ((tp & 3) << MEMTYPE_SHIFT); };
};
struct MemoryBlock {
uint32 address = 0;
byte data[256];
MemoryBlock() {
address = 0;
memset(data, 0, sizeof(data));
}
};
struct OpLog {
uint32 addr;
OP op;
uint32 sp;
};
struct MemAccess {
MemAccess(VM &vm): _vm(vm) {};
VM &_vm;
MemoryBlock *_currentBlock = nullptr;
uint8 getU8(uint32 address);
uint32 getU32(uint32 address);
void setU8(uint32 address, uint8 val);
void setU32(uint32 address, uint32 val);
void reset() {
_currentBlock = nullptr;
}
};
class Context {
public:
Context(VM &vm): _vm(vm), _readAccess(vm), _writeAccess(vm) {};
Common::String getString(int memtype, uint32 offset, uint32 maxLen = 256);
Common::String getString(const ValAddr &addr, uint32 maxLen = 256);
uint32 execute(uint32 scriptAddress, byte *storage = nullptr);
void push32(uint32 val);
uint32 pop32();
void pushReg(ValAddr reg);
ValAddr popReg();
uint32 getMem32(int memtype, uint32 offset);
uint32 getMem32(const ValAddr& addr);
uint8 getMem8(int memtype, uint32 offset);
uint8 getMem8(const ValAddr& addr);
void setMem32(int memtype, uint32 offset, uint32 val);
void setMem32(const ValAddr& addr, uint32 val);
void setMem8(int memtype, uint32 offset, uint8 val);
void setMem8(const ValAddr& addr, uint8 val);
public:
VM &_vm;
bool _inUse = false;
uint32 ESI = 0;
byte *EBX = nullptr;
ValAddr EAX;
ValAddr EDX;
ValAddr ECX;
uint32 SP = 0;
byte _stack[STACK_SIZE];
private:
MemAccess _readAccess;
MemAccess _writeAccess;
};
typedef void (* CallDispatcher)(void *object, Context *state, uint32 funcID);
public:
friend class Context;
public:
VM(void *obj, CallDispatcher dispatcher): _memAccess(*this), _threads{*this, *this}, _callFuncs(dispatcher), _callingObject(obj) {};
inline MemAccess &memory() {
return _memAccess;
};
void clearMemory();
void writeMemory(uint32 address, const byte* data, uint32 dataSize);
void zeroMemory(uint32 address, uint32 count);
MemoryBlock *findMemoryBlock(uint32 address);
MemoryBlock *createBlock(uint32 address);
Common::Array<byte> readMemBlocks(uint32 address, uint32 count);
void readMemBlocks(byte *dst, uint32 address, uint32 count);
Common::String readMemString(uint32 address, uint32 maxLen = 256);
uint32 doScript(uint32 scriptAddress, byte *storage = nullptr);
static int32 getS32(const void *);
static uint32 getU32(const void *);
static void setU32(void *, uint32 val);
Common::String decodeOp(uint32 address, int *size = nullptr);
Common::String disassembly(uint32 address);
Common::String opLog(const Common::Array<OpLog> &log);
void printDisassembly(uint32 address);
protected:
Common::HashMap<uint32, MemoryBlock> _memMap;
MemAccess _memAccess;
Context _threads[THREADS_COUNT];
CallDispatcher const _callFuncs = nullptr;
void * const _callingObject = nullptr;
public:
bool _interrupt = false;
};
}
#endif