scummvm/engines/glk/alan3/inter.cpp
2019-07-06 17:24:37 -07:00

1415 lines
35 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "glk/alan3/inter.h"
#include "glk/alan3/alan3.h"
#include "glk/alan3/current.h"
#include "glk/alan3/exe.h"
#include "glk/alan3/syserr.h"
#include "glk/alan3/debug.h"
#include "glk/alan3/glkio.h"
#include "glk/alan3/options.h"
#include "glk/alan3/save.h"
#include "glk/alan3/memory.h"
#include "glk/alan3/output.h"
#include "glk/alan3/score.h"
#include "glk/alan3/params.h"
#include "glk/alan3/instance.h"
#include "glk/alan3/container.h"
#include "glk/alan3/location.h"
#include "glk/alan3/compatibility.h"
#ifdef HAVE_GLK
#define MAP_STDIO_TO_GLK
#include "glk/alan3/glkio.h"
#endif
namespace Glk {
namespace Alan3 {
bool stopAtNextLine = FALSE;
bool fail = FALSE;
/* PRIVATE DATA */
static int pc;
static Stack stack = NULL;
static void (*interpreterMock)(Aaddr adr) = NULL;
/*======================================================================*/
void setInterpreterMock(void (*mock)(Aaddr adr)) {
interpreterMock = mock;
}
/*======================================================================*/
void setInterpreterStack(Stack theStack) {
stack = theStack;
}
/*----------------------------------------------------------------------*/
static void traceInstruction(const char *str, ...) {
va_list args;
if (traceInstructionOption) {
va_start(args, str);
Common::String msg = Common::String::format(str, args);
va_end(args);
printf("%s", msg.c_str());
}
}
/*----------------------------------------------------------------------*/
static void traceSkip() {
printf("\n : \t\t\t\t\t\t\t");
}
/*----------------------------------------------------------------------*/
static void interpretIf(Aword v) {
int lev = 1;
Aword i;
if (!v) {
/* Skip to next ELSE or ENDIF on same level */
if (traceInstructionOption) traceSkip();
while (TRUE) {
i = memory[pc++];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_ELSE:
if (lev == 1) {
if (traceInstructionOption)
printf("\n%4x: ELSE\t\t\t\t\t\t", pc);
return;
}
break;
case I_IF:
lev++;
break;
case I_ENDIF:
lev--;
if (lev == 0) {
if (traceInstructionOption)
printf("\n%4x: ENDIF\t\t\t\t\t\t", pc);
return;
}
break;
}
}
}
}
/*----------------------------------------------------------------------*/
static void interpretElse(void) {
int lev = 1;
Aword i;
if (traceInstructionOption) traceSkip();
while (TRUE) {
/* Skip to ENDIF on the same level */
i = memory[pc++];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_ENDIF:
lev--;
if (lev == 0) return;
break;
case I_IF:
lev++;
break;
}
}
}
/*----------------------------------------------------------------------*/
static void goToLOOPEND(void) {
int level = 1;
int i;
if (traceInstructionOption) traceSkip();
while (TRUE) {
/* Skip past LOOPEND on the same level */
i = memory[pc];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_LOOPEND:
level--;
if (level == 0)
return;
break;
case I_LOOP:
level++;
break;
}
pc++;
}
}
/*----------------------------------------------------------------------*/
static void jumpBackToStartOfMatchingLOOP(void) {
int level = 1;
int i;
if (traceInstructionOption) traceSkip();
pc--; /* Ignore the instruction we're on */
while (TRUE) {
/* Skip back past LOOP on the same level */
i = memory[--pc];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_LOOPEND:
level++;
break;
case I_LOOP:
level--;
if (level == 0) {
return;
}
break;
}
}
}
/*----------------------------------------------------------------------*/
static void nextLoop(void) {
goToLOOPEND();
}
/*----------------------------------------------------------------------*/
static void endLoop(Aint index, Aint limit) {
if (index < limit) {
index++;
push(stack, limit);
push(stack, index);
jumpBackToStartOfMatchingLOOP();
if (traceInstructionOption)
printf("\n%4x: LOOP\t\t\t\t\t\t", pc);
pc++;
}
}
/*----------------------------------------------------------------------*/
static void stackDup(void) {
push(stack, top(stack));
}
/*----------------------------------------------------------------------*/
static void depexec(Aword v) {
int lev = 1;
Aword i;
const char *instructionString = "DEPELSE";
if (!v) {
/* The expression was not true, skip to next CASE on the same
level which could be a DEPCASE or DEPELSE */
if (traceInstructionOption) printf("\n : ");
while (TRUE) {
i = memory[pc++];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_DEPEND:
lev++;
break;
case I_ENDDEP:
if (lev == 1) {
pc--;
if (traceInstructionOption)
printf("\n%4x: ENDDEP", pc);
return;
} else
lev--;
break;
case I_DEPCASE:
instructionString = "DEPCASE";
// fall through
case I_DEPELSE:
if (lev == 1) {
if (traceInstructionOption)
printf("\n%4x: %s", pc, instructionString);
return;
}
break;
}
}
}
}
/*----------------------------------------------------------------------*/
static void depcase(void) {
int lev = 1;
Aword i;
/*
We have just executed a DEPCASE/DEPELSE clause as a result of a
DEPCASE catching so skip to end of DEPENDING block (next DEPEND
on same level) then return.
*/
if (traceInstructionOption) printf("\n : ");
while (TRUE) {
i = memory[pc++];
if (I_CLASS(i) == (Aword)C_STMOP)
switch (I_OP(i)) {
case I_DEPEND:
lev++;
break;
case I_ENDDEP:
lev--;
if (lev == 0) {
pc--;
return;
}
break;
}
}
}
/*----------------------------------------------------------------------*/
static const char *booleanValue(Abool value) {
if (value) return " TRUE";
else return " FALSE";
}
/*----------------------------------------------------------------------*/
static const char *stringValue(Aptr address) {
static char string[1000];
sprintf(string, "0x%lx (\"%s\")\t\t", (unsigned long) address, (char *)fromAptr(address));
return string;
}
/*----------------------------------------------------------------------*/
static const char *pointerValue(Aptr address) {
static char string[100];
sprintf(string, "@%6lx", (unsigned long) address);
return string;
}
/*----------------------------------------------------------------------*/
static void traceStringTopValue() {
if (traceInstructionOption)
printf("\t=%s", stringValue(top(stack)));
}
/*----------------------------------------------------------------------*/
static void tracebooleanTopValue() {
if (traceInstructionOption) {
if (top(stack)) printf("\t=TRUE\t");
else printf("\t=FALSE\t");
}
}
/*----------------------------------------------------------------------*/
static void traceIntegerTopValue() {
if (traceInstructionOption)
printf("\t=%ld\t", (long)top(stack));
}
/*----------------------------------------------------------------------*/
static void tracePointerTopValue() {
if (traceInstructionOption)
printf("\t=%s\t", pointerValue(top(stack)));
}
/*----------------------------------------------------------------------*/
static void traceInstanceTopValue(CONTEXT) {
if (traceInstructionOption) {
printf("\t=%ld ('", (long)top(stack));
CALL1(traceSay, top(stack))
printf("')");
if (traceStackOption)
printf("\n\t\t\t\t\t\t\t");
}
}
/*----------------------------------------------------------------------*/
static const char *transitivityFlag(ATrans value) {
switch (value) {
case TRANSITIVE:
return "Transitive";
case DIRECT:
return "Direct";
case INDIRECT:
return "Indirect";
}
syserr("Unexpected transitivity");
return "ERROR";
}
/*----------------------------------------------------------------------*/
static const char *printForm(SayForm form) {
switch (form) {
case SAY_SIMPLE:
return "-";
case SAY_INDEFINITE:
return "An";
case SAY_DEFINITE:
return "The";
case SAY_NEGATIVE:
return "No";
case SAY_PRONOUN:
return "It";
}
return "**Unknown!!***";
}
static Aaddr invocation[1000];
int recursionDepth = 0;
/*----------------------------------------------------------------------*/
static void checkForRecursion(Aaddr adr) {
int i;
for (i = 0; i < recursionDepth; i++)
if (invocation[i] == adr)
apperr("Interpreter recursion.");
invocation[recursionDepth++] = adr;
if (recursionDepth > 1000)
syserr("Interpreter call stack too deep.");
}
static bool skipStackDump = FALSE; /* Need to be able to skip it for LINE */
/*----------------------------------------------------------------------*/
static bool stillOnSameLine(Aint line, Aint file) {
return line != current.sourceLine || file != current.sourceFile;
}
/*======================================================================*/
void interpret(CONTEXT, Aaddr adr) {
Aaddr oldpc;
Aword i;
/* Check for mock implementation */
if (interpreterMock != NULL) {
interpreterMock(adr);
return;
}
/* Sanity checks: */
if (adr == 0) syserr("Interpreting at address 0.");
checkForRecursion(adr);
if (traceInstructionOption)
printf("\n++++++++++++++++++++++++++++++++++++++++++++++++++");
oldpc = pc;
pc = adr;
while (TRUE) {
if (pc > memTop)
syserr("Interpreting outside program.");
i = memory[pc++];
switch (I_CLASS(i)) {
case C_CONST:
if (tracePushOption) printf("\n%4x: PUSH \t%7ld\t\t\t\t\t", pc - 1, (long)I_OP(i));
push(stack, I_OP(i));
if (tracePushOption && traceStackOption)
dumpStack(stack);
break;
case C_CURVAR:
if (traceInstructionOption) printf("\n%4x: ", pc - 1);
switch (I_OP(i)) {
case V_PARAM:
if (traceInstructionOption) printf("PARAM \t%7ld\t\t\t\t=%ld\t", (long)top(stack),
(long)globalParameters[top(stack) - 1].instance);
push(stack, globalParameters[pop(stack) - 1].instance);
break;
case V_CURLOC:
if (traceInstructionOption) printf("CURLOC \t\t\t\t\t=%d\t", current.location);
push(stack, current.location);
break;
case V_CURACT:
if (traceInstructionOption) printf("CURACT \t\t\t\t\t=%d\t", current.actor);
push(stack, current.actor);
break;
case V_CURVRB:
if (traceInstructionOption) printf("CURVRB \t\t\t\t\t=%d\t", current.verb);
push(stack, current.verb);
break;
case V_CURRENT_INSTANCE:
if (traceInstructionOption) printf("CURINS \t\t\t\t\t=%d\t", current.instance);
push(stack, current.instance);
break;
case V_SCORE:
if (traceInstructionOption) printf("CURSCORE \t\t\t\t\t=%d\t", current.score);
push(stack, current.score);
break;
case V_MAX_INSTANCE: {
int instanceMax = isPreBeta3(header->version) ? header->instanceMax : header->instanceMax - 1;
if (traceInstructionOption) printf("MAXINSTANCE \t\t\t\t=%d\t", instanceMax);
push(stack, instanceMax);
break;
}
default:
syserr("Unknown CURVAR instruction.");
break;
}
if (traceStackOption)
dumpStack(stack);
break;
case C_STMOP:
if (traceInstructionOption) printf("\n%4x: ", pc - 1);
switch (I_OP(i)) {
case I_DUP:
if (traceInstructionOption)
printf("DUP\t\t\t\t\t\t");
stackDup();
break;
case I_DUPSTR:
if (traceInstructionOption)
printf("DUPSTR\t\t\t\t\t\t");
push(stack, toAptr(strdup((char *)fromAptr(top(stack)))));
break;
case I_POP: {
Aptr top = pop(stack);
if (traceInstructionOption)
printf("POP\t%7ld", (long)top);
break;
}
case I_LINE: {
Aint line = pop(stack);
Aint file = pop(stack);
traceInstruction("LINE\t%7ld, %7ld\t\t\t", (long)file, (long)line);
if (traceStackOption)
dumpStack(stack);
skipStackDump = TRUE;
if (line != 0) {
bool atNext = stopAtNextLine && line != current.sourceLine;
bool atBreakpoint = breakpointIndex(file, line) != -1;
if (traceSourceOption && stillOnSameLine(line, file)) {
if (col != 1 || traceInstructionOption)
printf("\n");
showSourceLine(file, line);
if (!traceInstructionOption)
printf("\n");
}
current.sourceLine = line;
current.sourceFile = file;
if (atNext || atBreakpoint) {
stopAtNextLine = FALSE;
CALL3(debug, TRUE, line, file)
}
}
break;
}
case I_PRINT: {
Aint fpos = pop(stack);
Aint len = pop(stack);
if (traceInstructionOption) {
printf("PRINT \t%7ld, %7ld\t\"", (long)fpos, (long)len);
col = 41; /* To break lines better! */
}
print(fpos, len);
if (traceInstructionOption) {
printf("\"");
if (traceStackOption)
printf("\n\t\t\t\t\t\t\t");
}
break;
}
case I_STYLE: {
Aint style = pop(stack);
if (traceInstructionOption) {
printf("STYLE \t%7ld\t\t\"", (long)style);
}
g_io->setStyle(style);
break;
}
case I_SYSTEM: {
Aint fpos = pop(stack);
Aint len = pop(stack);
if (traceInstructionOption) {
printf("SYSTEM \t%7ld, %7ld\t\"", (long)fpos, (long)len);
col = 34; /* To format it better! */
}
sys(fpos, len);
if (traceInstructionOption)
printf("\"\t\t\t\t\t\t");
break;
}
case I_GETSTR: {
Aint fpos = pop(stack);
Aint len = pop(stack);
if (traceInstructionOption)
printf("GETSTR\t%7ld, %7ld", (long)fpos, (long)len);
push(stack, toAptr(getStringFromFile(fpos, len)));
traceStringTopValue();
break;
}
case I_QUIT: {
if (traceInstructionOption)
printf("QUIT\t\t\t\t\t\t");
CALL0(quitGame)
break;
}
case I_LOOK: {
if (traceInstructionOption)
printf("LOOK\t\t\t\t\t\t");
CALL0(look)
break;
}
case I_SAVE: {
if (traceInstructionOption)
printf("SAVE\t\t\t\t\t\t");
(void)g_vm->saveGame();
break;
}
case I_RESTORE: {
if (traceInstructionOption)
printf("RESTORE\t\t\t\t\t\t");
(void)g_vm->loadGame();
break;
}
case I_RESTART: {
if (traceInstructionOption)
printf("RESTART\t\t\t\t\t\t");
CALL0(restartGame)
break;
}
case I_SCORE: {
Aint sc = pop(stack);
if (traceInstructionOption)
printf("SCORE \t%7ld\t\t=%ld\t\t\t", (long)sc, (long)scores[sc - 1]);
score(sc);
break;
}
case I_VISITS: {
Aint v = pop(stack);
if (traceInstructionOption)
printf("VISITS \t%7ld\t\t\t\t\t", (long)v);
visits(v);
break;
}
case I_LIST: {
Aint cnt = pop(stack);
if (traceInstructionOption)
printf("LIST \t%7ld\t\t\t\t\t", (long)cnt);
CALL1(list, cnt)
break;
}
case I_EMPTY: {
Aint cnt = pop(stack);
Aint whr = pop(stack);
if (traceInstructionOption)
printf("EMPTY \t%7ld, %7ld\t\t\t\t", (long)cnt, (long)whr);
CALL2(empty, cnt, whr)
break;
}
case I_SCHEDULE: {
Aint event = pop(stack);
Aint where = pop(stack);
Aint after = pop(stack);
if (traceInstructionOption)
printf("SCHEDULE \t%7ld, %7ld, %7ld\t\t\t\t", (long)event, (long)where, (long)after);
schedule(event, where, after);
break;
}
case I_CANCEL: {
Aint event = pop(stack);
if (traceInstructionOption)
printf("CANCEL \t%7ld\t\t\t\t", (long)event);
cancelEvent(event);
break;
}
case I_MAKE: {
Aint atr = pop(stack);
Aid id = pop(stack);
Abool val = pop(stack);
if (traceInstructionOption)
printf("MAKE \t%7ld, %7ld, %s\t\t\t", (long)id, (long)atr, booleanValue(val));
setInstanceAttribute(id, atr, val);
break;
}
case I_SET: {
Aint atr = pop(stack);
Aid id = pop(stack);
Aptr val = pop(stack);
if (traceInstructionOption) {
printf("SET \t%7ld, %7ld, %7ld\t\t\t\t", (long)id, (long)atr, (long)val);
}
setInstanceAttribute(id, atr, val);
break;
}
case I_SETSTR: {
Aint atr = pop(stack);
Aid id = pop(stack);
Aptr str = pop(stack);
if (traceInstructionOption) {
printf("SETSTR\t%7ld, %7ld, %s\t\t\t\t", (long)id, (long)atr, stringValue(str));
}
setInstanceStringAttribute(id, atr, (char *)fromAptr(str));
break;
}
case I_SETSET: {
Aint atr = pop(stack);
Aid id = pop(stack);
Aptr set = pop(stack);
if (traceInstructionOption) {
printf("SETSET\t%7ld, %7ld, %7s\t\t", (long)id, (long)atr, pointerValue(set));
}
setInstanceSetAttribute(id, atr, set);
break;
}
case I_NEWSET: {
Set *set = newSet(0);
if (traceInstructionOption) {
printf("NEWSET\t\t\t");
}
push(stack, toAptr(set));
tracePointerTopValue();
break;
}
case I_UNION: {
Aptr set2 = pop(stack);
Aptr set1 = pop(stack);
if (traceInstructionOption) {
printf("UNION\t%7ld, %7ld\t\t\t\t", (long)set1, (long)set2);
}
push(stack, toAptr(setUnion((Set *)fromAptr(set1), (Set *)fromAptr(set2))));
tracePointerTopValue();
freeSet((Set *)fromAptr(set1));
freeSet((Set *)fromAptr(set2));
break;
}
case I_INCR: {
Aint step = pop(stack);
if (traceInstructionOption) {
printf("INCR\t%7ld", (long)step);
}
push(stack, pop(stack) + step);
traceIntegerTopValue();
break;
}
case I_DECR: {
Aint step = pop(stack);
if (traceInstructionOption) {
printf("DECR\t%7ld\t\t\t\t\t", (long)step);
}
push(stack, pop(stack) - step);
traceIntegerTopValue();
break;
}
case I_INCLUDE: {
Aint member = pop(stack);
if (traceInstructionOption) {
printf("INCLUDE\t%7ld\t\t\t\t\t", (long)member);
}
addToSet((Set *)fromAptr(top(stack)), member);
break;
}
case I_EXCLUDE: {
Aint member = pop(stack);
if (traceInstructionOption) {
printf("EXCLUDE\t%7ld", (long)member);
}
removeFromSet((Set *)fromAptr(top(stack)), member);
break;
}
case I_SETSIZE: {
Set *set = (Set *)fromAptr(pop(stack));
if (traceInstructionOption)
printf("SETSIZE\t%7p\t\t", set);
push(stack, setSize(set));
if (traceInstructionOption)
traceIntegerTopValue();
break;
}
case I_SETMEMB: {
Set *set = (Set *)fromAptr(pop(stack));
Aint index = pop(stack);
if (traceInstructionOption)
printf("SETMEMB\t%7p, %7ld", set, (long)index);
push(stack, getSetMember(set, index));
if (traceInstructionOption)
traceIntegerTopValue();
break;
}
case I_CONTSIZE: {
Abool transitivity = pop(stack);
Aint container = pop(stack);
if (traceInstructionOption)
printf("CONTSIZE\t%7ld, %7s\t", (long)container, transitivityFlag((ATrans)transitivity));
push(stack, containerSize(container, (ATrans)transitivity));
if (traceInstructionOption)
traceIntegerTopValue();
break;
}
case I_CONTMEMB: {
Abool transitivity = pop(stack);
Aint container = pop(stack);
Aint index = pop(stack);
if (traceInstructionOption)
printf("CONTMEMB\t%7ld, %7ld, %7s", (long)container, (long)index, transitivityFlag((ATrans)transitivity));
push(stack, getContainerMember(container, index, transitivity));
if (traceInstructionOption)
traceIntegerTopValue();
break;
}
case I_ATTRIBUTE: {
Aint atr = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("ATTRIBUTE %7ld, %7ld\t", (long)id, (long)atr);
push(stack, getInstanceAttribute(id, atr));
traceIntegerTopValue();
break;
}
case I_ATTRSTR: {
Aint atr = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("ATTRSTR \t%7ld, %7ld", (long)id, (long)atr);
push(stack, toAptr(getInstanceStringAttribute(id, atr)));
traceStringTopValue();
break;
}
case I_ATTRSET: {
Aint atr = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("ATTRSET \t%7ld, %7ld", (long)id, (long)atr);
push(stack, toAptr(getInstanceSetAttribute(id, atr)));
tracePointerTopValue();
break;
}
case I_SHOW: {
Aint image = pop(stack);
Aint align = pop(stack);
if (traceInstructionOption)
printf("SHOW \t%7ld, %7ld\t\t\t\t", (long)image, (long)align);
g_io->showImage(image, align);
break;
}
case I_PLAY: {
Aint sound = pop(stack);
if (traceInstructionOption)
printf("PLAY \t%7ld\t\t\t\t", (long)sound);
g_io->playSound(sound);
break;
}
case I_LOCATE: {
Aid id = pop(stack);
Aint whr = pop(stack);
if (traceInstructionOption)
printf("LOCATE \t%7ld, %7ld\t\t\t", (long)id, (long)whr);
CALL2(locate, id, whr)
break;
}
case I_WHERE: {
Abool transitivity = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("WHERE \t%7ld, %7s", (long)id, transitivityFlag((ATrans)transitivity));
push(stack, where(id, (ATrans)transitivity));
CALL0(traceInstanceTopValue);
break;
}
case I_LOCATION: {
Aid id = pop(stack);
if (traceInstructionOption)
printf("LOCATION \t%7ld\t\t", (long)id);
push(stack, locationOf(id));
CALL0(traceInstanceTopValue)
break;
}
case I_HERE: {
Abool transitivity = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("HERE \t%7ld, %s\t\t\t", (long)id, transitivityFlag((ATrans)transitivity));
push(stack, isHere(id, (ATrans)transitivity));
tracebooleanTopValue();
break;
}
case I_NEARBY: {
Abool transitivity = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("NEARBY \t%7ld, %s\t\t\t", (long)id, transitivityFlag((ATrans)transitivity));
push(stack, isNearby(id, (ATrans)transitivity));
tracebooleanTopValue();
break;
}
case I_NEAR: {
Abool transitivity = pop(stack);
Aint other = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("NEAR \t%7ld, %7ld, %s\t\t\t", (long)id, (long)other, transitivityFlag((ATrans)transitivity));
push(stack, isNear(id, other, (ATrans)transitivity));
tracebooleanTopValue();
break;
}
case I_AT: {
Abool transitivity = pop(stack);
Aint other = pop(stack);
Aint instance = pop(stack);
if (traceInstructionOption)
printf("AT \t%7ld, %7ld, %s", (long)instance, (long)other, transitivityFlag((ATrans)transitivity));
push(stack, isAt(instance, other, (ATrans)transitivity));
tracebooleanTopValue();
break;
}
case I_IN: {
Abool transitivity = pop(stack);
Aint cnt = pop(stack);
Aint obj = pop(stack);
if (traceInstructionOption)
printf("IN \t%7ld, %7ld, %s", (long)obj, (long)cnt, transitivityFlag((ATrans)transitivity));
push(stack, isIn(obj, cnt, (ATrans)transitivity));
tracebooleanTopValue();
break;
}
case I_INSET: {
Aptr set = pop(stack);
Aword element = pop(stack);
if (traceInstructionOption)
printf("INSET \t%7ld, %7ld", (long)element, (long)set);
push(stack, inSet((Set *)fromAptr(set), element));
freeSet((Set *)fromAptr(set));
tracebooleanTopValue();
break;
}
case I_USE: {
Aid act = pop(stack);
Aint scr = pop(stack);
if (traceInstructionOption)
printf("USE \t%7ld, %7ld\t\t\t\t", (long)act, (long)scr);
CALL2(use, act, scr)
break;
}
case I_STOP: {
Aid actor = pop(stack);
if (traceInstructionOption)
printf("STOP \t%7ld\t\t\t\t\t", (long)actor);
stop(actor);
break;
}
case I_DESCRIBE: {
Aid id = pop(stack);
if (traceInstructionOption) {
printf("DESCRIBE \t%7ld\t\t\t", (long)id);
col = 41; /* To format it better! */
}
CALL1(describe, id)
if (traceInstructionOption)
printf("\n\t\t\t\t\t\t");
break;
}
case I_SAY: {
Aint form = pop(stack);
Aid id = pop(stack);
if (traceInstructionOption)
printf("SAY\t%7s, %7ld\t\t\t", printForm((SayForm)form), (long)id);
if (form == SAY_SIMPLE) {
CALL1(say, id)
} else {
CALL2(sayForm, id, (SayForm)form)
}
if (traceInstructionOption)
printf("\t\t\t\t\t\t\t");
break;
}
case I_SAYINT: {
Aword val = pop(stack);
if (traceInstructionOption)
printf("SAYINT\t%7ld\t\t\t\"", (long)val);
sayInteger(val);
if (traceInstructionOption)
printf("\"\n\t\t\t\t\t\t\t");
break;
}
case I_SAYSTR: {
Aptr sayAddr = pop(stack);
if (traceInstructionOption)
printf("SAYSTR\t%7ld\t\ty\t", (long)sayAddr);
sayString((char *)fromAptr(sayAddr));
if (traceInstructionOption)
printf("\n\t\t\t\t\t\t");
break;
}
case I_IF: {
Aword v = pop(stack);
if (traceInstructionOption)
printf("IF \t%s\t\t\t\t\t", booleanValue(v));
interpretIf(v);
break;
}
case I_ELSE: {
if (traceInstructionOption)
printf("ELSE\t\t\t\t\t\t");
interpretElse();
break;
}
case I_ENDIF: {
if (traceInstructionOption)
printf("ENDIF\t\t\t\t\t\t");
break;
}
case I_AND: {
Aword rh = pop(stack);
Aword lh = pop(stack);
if (traceInstructionOption)
printf("AND \t%s, %s", booleanValue(lh), booleanValue(rh));
push(stack, lh && rh);
tracebooleanTopValue();
break;
}
case I_OR: {
Aword rh = pop(stack);
Aword lh = pop(stack);
if (traceInstructionOption)
printf("OR \t%s, %s", booleanValue(lh), booleanValue(rh));
push(stack, lh || rh);
tracebooleanTopValue();
break;
}
case I_NE: {
Aword rh = pop(stack);
Aword lh = pop(stack);
if (traceInstructionOption)
printf("NE \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh != rh);
tracebooleanTopValue();
break;
}
case I_EQ: {
Aword rh = pop(stack);
Aword lh = pop(stack);
if (traceInstructionOption)
printf("EQ \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh == rh);
tracebooleanTopValue();
break;
}
case I_STREQ: {
Aptr rh = pop(stack);
Aptr lh = pop(stack);
if (traceInstructionOption)
printf("STREQ \t0x%lx, 0x%lx", (long)lh, (long)rh);
push(stack, streq((char *)fromAptr(lh), (char *)fromAptr(rh)));
tracebooleanTopValue();
if (traceInstructionOption)
printf("\t");
deallocate(fromAptr(lh));
deallocate(fromAptr(rh));
break;
}
case I_STREXACT: {
Aptr rh = pop(stack);
Aptr lh = pop(stack);
if (traceInstructionOption)
printf("STREXACT \t0x%lx, 0x%lx", (long)lh, (long)rh);
push(stack, strcmp((char *)fromAptr(lh), (char *)fromAptr(rh)) == 0);
tracebooleanTopValue();
deallocate(fromAptr(lh));
deallocate(fromAptr(rh));
break;
}
case I_LE: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("LE \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh <= rh);
tracebooleanTopValue();
break;
}
case I_GE: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("GE \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh >= rh);
tracebooleanTopValue();
break;
}
case I_LT: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("LT \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh < rh);
tracebooleanTopValue();
break;
}
case I_GT: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("GT \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh > rh);
tracebooleanTopValue();
break;
}
case I_PLUS: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("PLUS \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh + rh);
traceIntegerTopValue();
break;
}
case I_MINUS: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("MINUS \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh - rh);
traceIntegerTopValue();
break;
}
case I_MULT: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("MULT \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh * rh);
traceIntegerTopValue();
break;
}
case I_DIV: {
Aint rh = pop(stack);
Aint lh = pop(stack);
if (traceInstructionOption)
printf("DIV \t%7ld, %7ld", (long)lh, (long)rh);
push(stack, lh / rh);
traceIntegerTopValue();
break;
}
case I_NOT: {
Aword val = pop(stack);
if (traceInstructionOption)
printf("NOT \t%s\t\t\t", booleanValue(val));
push(stack, !val);
tracebooleanTopValue();
break;
}
case I_RND: {
Aint from = pop(stack);
Aint to = pop(stack);
if (traceInstructionOption)
printf("RANDOM \t%7ld, %7ld", (long)from, (long)to);
push(stack, randomInteger(from, to));
traceIntegerTopValue();
break;
}
case I_BTW: {
Aint high = pop(stack);
Aint low = pop(stack);
Aint val = pop(stack);
if (traceInstructionOption)
printf("BETWEEN \t%7ld, %7ld, %7ld", (long)val, (long)low, (long)high);
push(stack, between(val, low, high));
traceIntegerTopValue();
break;
}
/*------------------------------------------------------------* \
String functions
\*------------------------------------------------------------*/
case I_CONCAT: {
Aptr s2 = pop(stack);
Aptr s1 = pop(stack);
if (traceInstructionOption)
printf("CONCAT \t%s, %s", pointerValue(s1), pointerValue(s2));
push(stack, concat(s1, s2));
traceStringTopValue();
deallocate(fromAptr(s1));
deallocate(fromAptr(s2));
break;
}
case I_CONTAINS: {
Aptr substring = pop(stack);
Aptr string = pop(stack);
if (traceInstructionOption)
printf("CONTAINS \t%s, %s", pointerValue(string), pointerValue(substring));
push(stack, contains(string, substring));
traceIntegerTopValue();
deallocate(fromAptr(string));
deallocate(fromAptr(substring));
break;
}
case I_STRIP: {
Aint atr = pop(stack);
Aid id = pop(stack);
Aint words = pop(stack);
Aint count = pop(stack);
Aint first = pop(stack);
if (traceInstructionOption)
printf("STRIP \t%7ld, %7ld, %7ld, %7ld, %7ld", (long)first, (long)count, (long)words, (long)id, (long)atr);
push(stack, strip(first, count, words, id, atr));
traceStringTopValue();
break;
}
/*------------------------------------------------------------
Aggregation
------------------------------------------------------------*/
case I_MIN:
case I_SUM:
case I_MAX: {
Aint attribute = pop(stack);
Aint loopIndex = pop(stack);
Aint limit = pop(stack);
Aint aggregate = pop(stack);
switch (I_OP(i)) {
case I_MAX:
if (traceInstructionOption)
printf("MAX \t%7ld\t\t\t", (long)attribute);
if (aggregate < attribute)
push(stack, attribute);
else
push(stack, aggregate);
break;
case I_MIN:
if (traceInstructionOption)
printf("MIN \t%7ld\t\t\t", (long)attribute);
if (aggregate > attribute)
push(stack, attribute);
else
push(stack, aggregate);
break;
case I_SUM:
if (traceInstructionOption)
printf("SUM \t%7ld\t\t\t", (long)attribute);
push(stack, aggregate + attribute);
break;
}
traceIntegerTopValue();
push(stack, limit);
push(stack, loopIndex);
break;
}
case I_COUNT: {
Aint loopIndex = pop(stack);
Aint limit = pop(stack);
if (traceInstructionOption)
printf("COUNT\t\t\t");
push(stack, pop(stack) + 1);
traceIntegerTopValue();
push(stack, limit);
push(stack, loopIndex);
break;
}
case I_TRANSCRIPT: {
Aint on_or_off = pop(stack);
if (traceInstructionOption)
printf("TRANSCRIPT\t\t\t");
if (on_or_off)
startTranscript();
else
stopTranscript();
break;
}
/*------------------------------------------------------------
Depending On
------------------------------------------------------------*/
case I_DEPEND:
if (traceInstructionOption)
printf("DEPEND\t\t\t\t\t\t");
break;
case I_DEPCASE:
if (traceInstructionOption)
printf("DEPCASE\t\t\t\t\t\t");
depcase();
break;
case I_DEPEXEC: {
Aword v = pop(stack);
if (traceInstructionOption) {
printf("DEPEXEC \t\t\t");
if (v) printf(" TRUE");
else printf("FALSE");
printf("\t\t\t\t\t");
}
depexec(v);
break;
}
case I_DEPELSE:
if (traceInstructionOption)
printf("DEPELSE\t\t\t\t\t\t");
depcase();
break;
case I_ENDDEP:
if (traceInstructionOption)
printf("ENDDEP\t\t\t\t\t\t");
pop(stack);
break;
case I_ISA: {
Aid rh = pop(stack);
Aid lh = pop(stack);
if (traceInstructionOption)
printf("ISA \t%7ld, %7ld\t", (long)lh, (long)rh);
push(stack, isA(lh, rh));
tracebooleanTopValue();
break;
}
case I_FRAME: {
Aint size = pop(stack);
if (traceInstructionOption)
printf("FRAME \t%7ld\t\t\t\t\t", (long)size);
newFrame(stack, size);
break;
}
case I_GETLOCAL: {
Aint framesBelow = pop(stack);
Aint variableNumber = pop(stack);
if (traceInstructionOption)
printf("GETLOCAL \t%7ld, %7ld\t", (long)framesBelow, (long)variableNumber);
push(stack, getLocal(stack, framesBelow, variableNumber));
traceIntegerTopValue();
break;
}
case I_SETLOCAL: {
Aint framesBelow = pop(stack);
Aint variableNumber = pop(stack);
Aint value = pop(stack);
if (traceInstructionOption)
printf("SETLOCAL \t%7ld, %7ld, %7ld\t\t", (long)framesBelow, (long)variableNumber, (long)value);
setLocal(stack, framesBelow, variableNumber, value);
break;
}
case I_ENDFRAME: {
if (traceInstructionOption)
printf("ENDFRAME\t\t\t\t\t\t");
endFrame(stack);
break;
}
case I_LOOP: {
Aint index = pop(stack);
Aint limit = pop(stack);
if (traceInstructionOption)
printf("LOOP \t\t\t\t\t\t");
push(stack, limit);
push(stack, index);
if (index > limit)
goToLOOPEND();
break;
}
case I_LOOPNEXT: {
if (traceInstructionOption)
printf("LOOPNEXT\t\t\t\t\t\t");
nextLoop();
break;
}
case I_LOOPEND: {
Aint index = pop(stack);
Aint limit = pop(stack);
if (traceInstructionOption)
printf("LOOPEND\t\t\t\t\t\t");
endLoop(index, limit);
break;
}
case I_RETURN:
if (traceInstructionOption)
printf("RETURN\n--------------------------------------------------\n");
pc = oldpc;
goto exitInterpreter;
default:
syserr("Unknown STMOP instruction.");
break;
}
if (fail) {
pc = oldpc;
goto exitInterpreter;
}
if (traceStackOption) {
if (!skipStackDump)
dumpStack(stack);
skipStackDump = FALSE;
}
break;
default:
syserr("Unknown instruction class.");
break;
}
}
exitInterpreter:
recursionDepth--;
}
/*======================================================================*/
Aword evaluate(CONTEXT, Aaddr adr) {
R0CALL1(interpret, adr)
return pop(stack);
}
} // End of namespace Alan3
} // End of namespace Glk