scummvm/engines/private/code.cpp
2021-12-26 18:48:43 +01:00

429 lines
10 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/>.
*
*/
// Heavily inspired by hoc
// Copyright (C) AT&T 1995
// All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and
// its documentation for any purpose and without fee is hereby
// granted, provided that the above copyright notice appear in all
// copies and that both that the copyright notice and this
// permission notice and warranty disclaimer appear in supporting
// documentation, and that the name of AT&T or any of its entities
// not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
// THIS SOFTWARE.
#include "common/debug.h"
#include "common/hash-ptr.h"
#include "common/str.h"
#include "private/grammar.h"
#include "private/private.h"
#include "private/tokens.h"
namespace Private {
Gen::VM *Gen::g_vm;
void Gen::VM::run() {
Gen::execute(Gen::g_vm->_prog);
}
namespace Settings {
using namespace Gen;
SettingMaps *g_setts;
/* initialize setting for code generation */
void SettingMaps::init() {
_setting = (Setting *)malloc(sizeof(Setting));
memset((void *)_setting, 0, sizeof(Setting));
g_vm->_prog = (Inst *)&_setting->prog;
g_vm->_stack = (Datum *)&_setting->stack;
g_vm->_progp = Gen::g_vm->_prog;
g_vm->_stackp = Gen::g_vm->_stack;
}
void SettingMaps::save(const char *name) {
_map.setVal(name, _setting);
}
void SettingMaps::load(const Common::String &name) {
assert(_map.contains(name));
_setting = _map.getVal(name);
debugC(1, kPrivateDebugCode, "loading setting %s", name.c_str());
g_vm->_prog = (Inst *)&_setting->prog;
g_vm->_stack = (Datum *)&_setting->stack;
g_vm->_stackp = g_vm->_stack;
g_vm->_progp = g_vm->_prog;
}
} // end of namespace Settings
namespace Gen {
/* pop and return top elem from stack */
Datum pop() {
assert(!(g_vm->_stackp <= g_vm->_stack));
return *--g_vm->_stackp;
}
/* push d onto stack */
int push(const Datum &d) {
assert(!(g_vm->_stackp >= &g_vm->_stack[NSTACK]));
*g_vm->_stackp++ = d;
return 0;
}
/* push constant onto stack */
int constpush() {
Datum d;
Symbol *s = (Symbol *)*g_vm->_pc++;
d.type = NUM;
d.u.val = s->u.val;
debugC(1, kPrivateDebugCode, "pushing const %d with name %s", d.u.val, s->name->c_str());
push(d);
return 0;
}
int strpush() { /* push constant onto stack */
Datum d;
d.type = STRING;
Symbol *s = (Symbol *)*g_vm->_pc++;
d.u.str = s->u.str;
debugC(1, kPrivateDebugCode, "pushing const %s with name %s", d.u.str, s->name->c_str());
push(d);
return 0;
}
int varpush() { /* push variable onto stack */
Datum d;
d.type = NAME;
d.u.sym = (Symbol *)(*g_vm->_pc++);
debugC(1, kPrivateDebugCode, "var pushing %s", d.u.sym->name->c_str());
push(d);
return 0;
}
int funcpush() {
Datum s = pop();
Datum n = pop();
ArgArray args;
debugC(1, kPrivateDebugCode, "executing %s with %d params", s.u.str, n.u.val);
for (int i = 0; i < n.u.val; i++) {
Datum arg = pop();
args.insert(args.begin(), arg);
}
call(s.u.str, args);
return 0;
}
/* evaluate variable on stack */
int eval() {
Datum d = pop();
if (d.u.sym->type == NUM) {
d.type = NUM;
d.u.val = d.u.sym->u.val;
debugC(1, kPrivateDebugCode, "eval NUM returned %d", d.u.val);
} else if (d.u.sym->type == STRING) {
d.type = STRING;
d.u.str = d.u.sym->u.str;
debugC(1, kPrivateDebugCode, "eval STR returned %s", d.u.str);
} else if (d.u.sym->type == RECT) {
d.type = RECT;
d.u.rect = d.u.sym->u.rect;
debugC(1, kPrivateDebugCode, "eval RECT");
} else if (d.u.sym->type == NAME) {
debugC(1, kPrivateDebugCode, "eval NAME is noop");
// No evaluation until is absolutely needed
} else
assert(0);
push(d);
return 0;
}
/* add top two elems on stack */
int add() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
assert(d1.type == NUM);
assert(d2.type == NUM);
debugC(1, kPrivateDebugCode, "adding %d %d\n", d1.u.val, d2.u.val);
d1.u.val += d2.u.val;
push(d1);
return 0;
}
int negate() {
Datum d = pop();
int v = 0;
if (d.type == NAME) {
d.u.sym = g_private->maps.lookupVariable(d.u.sym->name);
v = d.u.sym->u.val;
d.type = NUM;
} else if (d.type == NUM) {
v = d.u.val;
} else
assert(0);
debugC(1, kPrivateDebugCode, "negating %d\n", d.u.val);
d.u.val = !v;
push(d);
return 0;
}
int gt() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val > d2.u.val);
push(d1);
return 0;
}
int lt() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val < d2.u.val);
push(d1);
return 0;
}
int ge() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val >= d2.u.val);
push(d1);
return 0;
}
int le() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val <= d2.u.val);
push(d1);
return 0;
}
int eq() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val == d2.u.val);
push(d1);
return 0;
}
int ne() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val != d2.u.val);
push(d1);
return 0;
}
/* install one instruction or operand */
Inst *code(const Inst &f) {
//debugC(1, kPrivateDebugCode, "pushing code at %x", progp);
Inst *oprogp = g_vm->_progp;
assert(!(g_vm->_progp >= &g_vm->_prog[NPROG]));
*g_vm->_progp++ = f;
return oprogp;
}
int ifcode() {
Inst *savepc = g_vm->_pc; /* then part */
debugC(1, kPrivateDebugCode, "ifcode: evaluating condition");
execute(savepc + 3); /* condition */
Datum d = pop();
debugC(1, kPrivateDebugCode, "ifcode: selecting branch");
if (d.type == NAME) {
debugC(1, kPrivateDebugCode, "name %s", d.u.sym->name->c_str());
d.u.sym = g_private->maps.lookupVariable(d.u.sym->name);
d.u.val = d.u.sym->u.val;
}
if (d.u.val) {
debugC(1, kPrivateDebugCode, "ifcode: true branch");
execute(*((Inst **)(savepc)));
} else if (*((Inst **)(savepc + 1))) { /* else part? */
debugC(1, kPrivateDebugCode, "ifcode: false branch");
execute(*((Inst **)(savepc + 1)));
}
debugC(1, kPrivateDebugCode, "ifcode finished");
g_vm->_pc = *((Inst **)(savepc + 2)); /* next stmt */
return 0;
}
int randbool() {
Datum d = pop();
int v = g_private->getRandomBool(d.u.val);
d.u.val = v;
push(d);
return 0;
}
int fail() {
assert(0);
return 0;
}
/* run the machine */
void execute(Inst *p) {
for (g_vm->_pc = p; *(g_vm->_pc) != STOP;) {
(*(*(g_vm->_pc++)))();
}
}
} // End of namespace Gen
} // End of namespace Private