From bad3ecd643132a09422117b63a687bf758139c01 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 22 Jun 2019 12:19:13 -0700 Subject: GLK: ALAN2: Create jump context system to replace original setjmp This is basically a simplified version of the ScummVM coroutines, since we just need the ability to consistently break out to the main game loop when a call is made to the error method --- engines/glk/alan2/jumps.h | 70 ++++++++++++++++ engines/glk/alan2/main.cpp | 59 ++++++++------ engines/glk/alan2/main.h | 7 +- engines/glk/alan2/parse.cpp | 190 +++++++++++++++++++++++++------------------- engines/glk/alan2/parse.h | 4 +- 5 files changed, 221 insertions(+), 109 deletions(-) create mode 100644 engines/glk/alan2/jumps.h diff --git a/engines/glk/alan2/jumps.h b/engines/glk/alan2/jumps.h new file mode 100644 index 0000000000..b477354179 --- /dev/null +++ b/engines/glk/alan2/jumps.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +#ifndef GLK_ALAN2_JUMPS +#define GLK_ALAN2_JUMPS + +/* This provides a simplified version of the ScummVM coroutines to allow for automated + * breakouts to the main game loop from subroutinese rather than using unportable setjmps + */ + +namespace Glk { +namespace Alan2 { + +/** + * Context used for flagging when a break to the outer game loop + */ +struct Context { + bool _break; + Context() : _break(false) {} +}; + +#define CALL0(METHOD) { METHOD(context); if (context._break) return; } +#define CALL1(METHOD, P1) { METHOD(context, P1); if (context._break) return; } +#define CALL2(METHOD, P1, P2) { METHOD(context, P2); if (context._break) return; } +#define CALL3(METHOD, P1, P2, P3) { METHOD(context, P3); if (context._break) return; } +#define CALL4(METHOD, P1, P2, P3, P4) { METHOD(context, P4); if (context._break) return; } +#define FUNC0(METHOD, RET) { RET = METHOD(context); if (context._break) return; } +#define FUNC1(METHOD, RET, P1) { RET = METHOD(context, P1); if (context._break) return; } +#define FUNC2(METHOD, RET, P1, P2) { RET = METHOD(context, P2); if (context._break) return; } +#define FUNC3(METHOD, RET, P1, P2, P3) { RET = METHOD(context, P3); if (context._break) return; } +#define FUNC4(METHOD, RET, P1, P2, P3, P4) { RET = METHOD(context, P4); if (context._break) return; } + +#define R0CALL0(METHOD) { METHOD(context); if (context._break) return 0; } +#define R0CALL1(METHOD, P1) { METHOD(context, P1); if (context._break) return 0; } +#define R0CALL2(METHOD, P1, P2) { METHOD(context, P2); if (context._break) return 0; } +#define R0CALL3(METHOD, P1, P2, P3) { METHOD(context, P3); if (context._break) return 0; } +#define R0CALL4(METHOD, P1, P2, P3, P4) { METHOD(context, P4); if (context._break) return 0; } +#define R0FUNC0(METHOD, RET) { RET = METHOD(context); if (context._break) return 0; } +#define R0FUNC1(METHOD, RET, P1) { RET = METHOD(context, P1); if (context._break) return 0; } +#define R0FUNC2(METHOD, RET, P1, P2) { RET = METHOD(context, P2); if (context._break) return 0; } +#define R0FUNC3(METHOD, RET, P1, P2, P3) { RET = METHOD(context, P3); if (context._break) return 0; } +#define R0FUNC4(METHOD, RET, P1, P2, P3, P4) { RET = METHOD(context, P4); if (context._break) return 0; } + +#define CONTEXT Context &context +#define LONG_JUMP { context._break = true; return; } +#define LONG_JUMP0 { context._break = true; return 0; } + +} // End of namespace Alan2 +} // End of namespace Glk + +#endif diff --git a/engines/glk/alan2/main.cpp b/engines/glk/alan2/main.cpp index 6732bfb791..2dea4519f7 100644 --- a/engines/glk/alan2/main.cpp +++ b/engines/glk/alan2/main.cpp @@ -162,16 +162,15 @@ void syserr(const char *str) { error() Print an error message, force new player input and abort. - */ -void error(MsgKind msgno /* IN - The error message number */) { + /* IN - The error message number */ +void error(CONTEXT, MsgKind msgno) { if (msgno != MSGMAX) prmsg(msgno); wrds[wrdidx] = EOD; /* Force new player input */ dscrstkp = 0; /* Reset describe stack */ - //longjmp(jmpbuf,TRUE); - ::error("Error occurred"); + LONG_JUMP } @@ -677,13 +676,13 @@ static Boolean trycheck( Move hero in a direction. */ -void go(int dir) { +void go(CONTEXT, int dir) { ExtElem *ext; Boolean ok; Aword oldloc; ext = (ExtElem *) addrTo(locs[cur.loc - LOCMIN].exts); - if (locs[cur.loc - LOCMIN].exts != 0) + if (locs[cur.loc - LOCMIN].exts != 0) { while (!endOfTable(ext)) { if ((int)ext->code == dir) { ok = TRUE; @@ -722,7 +721,9 @@ void go(int dir) { } ext++; } - error(M_NO_WAY); + } + + CALL1(error, M_NO_WAY) } @@ -806,7 +807,7 @@ Boolean possible() { Execute the action commanded by hero. */ -static void do_it() { +static void do_it(CONTEXT) { AltElem *alt[MAXPARAMS + 2]; /* List of alt-pointers, one for each param */ Boolean done[MAXPARAMS + 2]; /* Is it done */ int i; /* Parameter index */ @@ -855,9 +856,10 @@ static void do_it() { for (i = 0; i < 2 || params[i - 2].code != EOD; i++) if (alt[i] != 0 && alt[i]->action != 0) break; - if (i >= 2 && params[i - 2].code == EOD) - /* Didn't find any code for this verb/object combination */ - error(M_CANT0); + if (i >= 2 && params[i - 2].code == EOD) { + // Didn't find any code for this verb/object combination + CALL1(error, M_CANT0) + } /* Perform actions! */ @@ -943,7 +945,7 @@ static void do_it() { such as THEM or lists of objects. */ -void action(ParamElem plst[] /* IN - Plural parameter list */) { +void action(CONTEXT, ParamElem plst[] /* IN - Plural parameter list */) { int i, mpos; char marker[10]; @@ -957,13 +959,14 @@ void action(ParamElem plst[] /* IN - Plural parameter list */) { for (i = 0; plst[i].code != EOD; i++) { params[mpos] = plst[i]; output(marker); - do_it(); + CALL0(do_it) if (plst[i + 1].code != EOD) para(); } params[mpos].code = 0; - } else - do_it(); + } else { + CALL0(do_it) + } } @@ -1282,14 +1285,14 @@ static void init() { Let the current actor move. If player, ask him. */ -static void movactor() { +static void movactor(CONTEXT) { ScrElem *scr; StepElem *step; ActElem *act = (ActElem *) &acts[cur.act - ACTMIN]; cur.loc = where(cur.act); if (cur.act == (int)HERO) { - parse(); + CALL0(parse) if (g_vm->shouldQuit()) return; fail = FALSE; /* fail only aborts one actor */ @@ -1416,17 +1419,23 @@ void run() { init(); /* Load, initialise and start the adventure */ - while (TRUE) { - if (dbgflg) - debug(); + Context ctx; + for (;;) { + if (!ctx._break) { + if (dbgflg) + debug(); - eventchk(); - cur.tick++; - // (void) setjmp(jmpbuf); + eventchk(); + cur.tick++; + } + + // Execution ends up here after calls to the error method // Move all characters - for (cur.act = ACTMIN; cur.act <= (int)ACTMAX; cur.act++) { - movactor(); + ctx._break = false; + for (cur.act = ACTMIN; cur.act <= (int)ACTMAX && !ctx._break; cur.act++) { + movactor(ctx); + if (g_vm->shouldQuit()) return; } diff --git a/engines/glk/alan2/main.h b/engines/glk/alan2/main.h index 9dffbc343a..9672899c81 100644 --- a/engines/glk/alan2/main.h +++ b/engines/glk/alan2/main.h @@ -27,6 +27,7 @@ #include "common/file.h" #include "glk/alan2/types.h" +#include "glk/alan2/jumps.h" namespace Glk { namespace Alan2 { @@ -100,7 +101,7 @@ extern Boolean needsp; extern void *allocate(unsigned long len); extern void terminate(int code); extern void usage(void); -extern void error(MsgKind msg); +extern void error(CONTEXT, MsgKind msg); extern void syserr(const char *msg); extern void statusline(void); extern void output(const char string[]); @@ -111,8 +112,8 @@ extern void newline(void); extern Boolean checklim(Aword cnt, Aword obj); extern Boolean possible(void); extern Boolean exitto(int to, int from); -extern void action(ParamElem *plst); -extern void go(int dir); +extern void action(CONTEXT, ParamElem *plst); +extern void go(CONTEXT, int dir); extern Boolean eot(Aword *adr); extern Boolean isObj(Aword x); diff --git a/engines/glk/alan2/parse.cpp b/engines/glk/alan2/parse.cpp index 24961c91e9..9e1c3400b3 100644 --- a/engines/glk/alan2/parse.cpp +++ b/engines/glk/alan2/parse.cpp @@ -89,7 +89,7 @@ static char isobuf[LISTLEN + 1]; /* The input buffer in ISO */ static Boolean eol = TRUE; /* Looking at End of line? Yes, initially */ -static void unknown(char token[]) { +static void unknown(CONTEXT, char token[]) { char *str = (char *)allocate((int)strlen(token) + 4); str[0] = '\''; @@ -98,29 +98,29 @@ static void unknown(char token[]) { output(str); free(str); eol = TRUE; - error(M_UNKNOWN_WORD); + CALL1(error, M_UNKNOWN_WORD) } static char *token; -static int lookup(char wrd[]) { +static int lookup(CONTEXT, char wrd[]) { int i; for (i = 0; !endOfTable(&dict[i]); i++) { if (strcmp(wrd, (char *) addrTo(dict[i].wrd)) == 0) return (i); } - unknown(wrd); - return (EOD); + R0CALL1(unknown, wrd) + return EOD; } /* IN - The string to convert to a number */ static int number(char tok[]) { int i; - sscanf(tok, "%d", &i); + (void)sscanf(tok, "%d", &i); return i; } @@ -185,7 +185,7 @@ static void agetline() { lin = 1; } -static void scan() { +static void scan(CONTEXT) { int i; int w; char *str; @@ -203,7 +203,8 @@ static void scan() { do { if (isISOLetter(token[0])) { (void) stringLower(token); - w = lookup(token); + FUNC1(lookup, w, token); + if (!isNoise(w)) wrds[i++] = w; } else if (isdigit(token[0])) { @@ -223,8 +224,9 @@ static void scan() { litValues[litCount++].value = (Aptr) str; } else if (token[0] == ',') { wrds[i++] = conjWord; - } else - unknown(token); + } else { + CALL1(unknown, token) + } wrds[i] = EOD; eol = (token = gettoken(NULL)) == NULL; } while (!eol); @@ -250,20 +252,22 @@ static void scan() { static int allLength; /* No. of objects matching 'all' */ -static void nonverb() { +static void nonverb(CONTEXT) { if (isDir(wrds[wrdidx])) { wrdidx++; - if (wrds[wrdidx] != EOD && !isConj(wrds[wrdidx])) - error(M_WHAT); - else - go(dict[wrds[wrdidx - 1]].code); + if (wrds[wrdidx] != EOD && !isConj(wrds[wrdidx])) { + CALL1(error, M_WHAT) + } else { + CALL1(go, dict[wrds[wrdidx - 1]].code) + } if (wrds[wrdidx] != EOD) wrdidx++; - } else - error(M_WHAT); + } else { + CALL1(error, M_WHAT) + } } -static void buildall(ParamElem list[]) { +static void buildall(CONTEXT, ParamElem list[]) { int i = 0; Boolean found = FALSE; @@ -273,13 +277,14 @@ static void buildall(ParamElem list[]) { list[i].code = o; list[i++].firstWord = EOD; } - if (!found) - error(M_WHAT_ALL); - else + if (!found) { + CALL1(error, M_WHAT_ALL) + } else { list[i].code = EOD; + } } -static void unambig(ParamElem plst[]) { +static void unambig(CONTEXT, ParamElem plst[]) { int i; Boolean found = FALSE; /* Adjective or noun found ? */ static ParamElem *refs; /* Entities referenced by word */ @@ -305,13 +310,14 @@ static void unambig(ParamElem plst[]) { wrdidx++; /* Use last object in previous command! */ for (i = lstlen(pparams) - 1; i >= 0 && (pparams[i].code == 0 || pparams[i].code >= LITMIN); i--); - if (i < 0) - error(M_WHAT_IT); + if (i < 0) { + CALL1(error, M_WHAT_IT) + } if (!isHere(pparams[i].code)) { params[0].code = pparams[i].code; params[0].firstWord = EOD; params[1].code = EOD; - error(M_NO_SUCH); + CALL1(error, M_NO_SUCH) } plst[0] = pparams[i]; plst[0].firstWord = EOD; /* No words used! */ @@ -345,7 +351,7 @@ static void unambig(ParamElem plst[]) { } wrdidx++; } else - error(M_NOUN); + CALL1(error, M_NOUN) } else if (found) { if (isNoun(wrds[wrdidx - 1])) { /* Perhaps the last word was also a noun? */ @@ -355,8 +361,9 @@ static void unambig(ParamElem plst[]) { lstcpy(plst, refs); else isect(plst, refs); - } else - error(M_NOUN); + } else { + CALL1(error, M_NOUN) + } } lastWord = wrdidx - 1; @@ -373,17 +380,18 @@ static void unambig(ParamElem plst[]) { params[0].firstWord = firstWord; /* Remember words for errors below */ params[0].lastWord = lastWord; params[1].code = EOD; /* But be sure to terminate */ - if (lstlen(plst) > 1) - error(M_WHICH_ONE); - else if (found && lstlen(plst) == 0) - error(M_NO_SUCH); + if (lstlen(plst) > 1) { + CALL1(error, M_WHICH_ONE) + } else if (found && lstlen(plst) == 0) { + CALL1(error, M_NO_SUCH) + } } else { plst[0].firstWord = firstWord; plst[0].lastWord = lastWord; } } -static void simple(ParamElem olst[]) { +static void simple(CONTEXT, ParamElem olst[]) { static ParamElem *tlst = NULL; int savidx = wrdidx; Boolean savplur = FALSE; @@ -400,13 +408,16 @@ static void simple(ParamElem olst[]) { if (!isHere(pmlst[i].code)) pmlst[i].code = 0; compact(pmlst); - if (lstlen(pmlst) == 0) - error(M_WHAT_THEM); + if (lstlen(pmlst) == 0) { + CALL1(error, M_WHAT_THEM) + } + lstcpy(olst, pmlst); olst[0].firstWord = EOD; /* No words used */ wrdidx++; } else { - unambig(olst); /* Look for unambigous noun phrase */ + // Look for unambigous noun phrase + CALL1(unambig, olst) if (lstlen(olst) == 0) { /* Failed! */ lstcpy(olst, tlst); wrdidx = savidx; @@ -440,7 +451,7 @@ static void simple(ParamElem olst[]) { entity tables. Particularly this goes for literals... */ -static void complex(ParamElem olst[]) { +static void complex(CONTEXT, ParamElem olst[]) { static ParamElem *alst = NULL; if (alst == NULL) @@ -448,21 +459,24 @@ static void complex(ParamElem olst[]) { if (isAll(wrds[wrdidx])) { plural = TRUE; - buildall(alst); /* Build list of all objects */ + // Build list of all objects + CALL1(buildall, alst) wrdidx++; if (wrds[wrdidx] != EOD && isBut(wrds[wrdidx])) { wrdidx++; - simple(olst); + CALL1(simple, olst) if (lstlen(olst) == 0) - error(M_AFTER_BUT); + CALL1(error, M_AFTER_BUT) sublst(alst, olst); if (lstlen(alst) == 0) - error(M_NOT_MUCH); + CALL1(error, M_NOT_MUCH) } lstcpy(olst, alst); allLength = lstlen(olst); - } else - simple(olst); /* Look for simple noun group */ + } else { + // Look for simple noun group + CALL1(simple, olst) + } } static Boolean claCheck(ClaElem *cla /* IN - The cla elem to check */) { @@ -494,7 +508,7 @@ static Boolean claCheck(ClaElem *cla /* IN - The cla elem to check */) { access to remote object), we need to remove non-present parameters */ -static void resolve(ParamElem plst[]) { +static void resolve(CONTEXT, ParamElem plst[]) { int i; if (allLength > 0) return; /* ALL has already done this */ @@ -505,11 +519,12 @@ static void resolve(ParamElem plst[]) { if (!isHere(plst[i].code)) { params[0] = plst[i]; /* Copy error param as first one for message */ params[1].code = EOD; /* But be sure to terminate */ - error(M_NO_SUCH); + CALL1(error, M_NO_SUCH) } } -static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by multiple */) { +/* OUT - List of params allowed by multiple */ +static void tryMatch(CONTEXT, ParamElem matchLst[]) { ElmElem *elms; /* Pointer to element list */ StxElem *stx; /* Pointer to syntax list */ ClaElem *cla; /* Pointer to class definitions */ @@ -526,8 +541,9 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul for (stx = stxs; !endOfTable(stx); stx++) if ((int)stx->code == vrbcode) break; - if (endOfTable(stx)) - error(M_WHAT); + if (endOfTable(stx)) { + CALL1(error, M_WHAT) + } elms = (ElmElem *) addrTo(stx->elms); @@ -536,37 +552,43 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul if (wrds[wrdidx] == EOD || isConj(wrds[wrdidx])) { while (!endOfTable(elms) && elms->code != EOS) elms++; - if (endOfTable(elms)) - error(M_WHAT); - else + if (endOfTable(elms)) { + CALL1(error, M_WHAT) + } else break; } else { /* A preposition? */ if (isPrep(wrds[wrdidx])) { while (!endOfTable(elms) && elms->code != dict[wrds[wrdidx]].code) elms++; - if (endOfTable(elms)) - error(M_WHAT); - else + if (endOfTable(elms)) { + CALL1(error, M_WHAT) + } else wrdidx++; } else { /* Must be a parameter! */ while (!endOfTable(elms) && elms->code != 0) elms++; - if (endOfTable(elms)) - error(M_WHAT); + if (endOfTable(elms)) { + CALL1(error, M_WHAT) + } /* Get it! */ plural = FALSE; - complex(tlst); - if (lstlen(tlst) == 0) /* No object!? */ - error(M_WHAT); - if ((elms->flags & OMNIBIT) == 0) /* Omnipotent parameter? */ + CALL1(complex, tlst) + if (lstlen(tlst) == 0) { + /* No object!? */ + CALL1(error, M_WHAT) + } + /* Omnipotent parameter? */ + if ((elms->flags & OMNIBIT) == 0) { /* If its not an omnipotent parameter, resolve by presence */ - resolve(tlst); + CALL1(resolve, tlst) + } if (plural) { - if ((elms->flags & MULTIPLEBIT) == 0) /* Allowed multiple? */ - error(M_MULTIPLE); - else { + /* Allowed multiple? */ + if ((elms->flags & MULTIPLEBIT) == 0) { + CALL1(error, M_MULTIPLE) + } else { /* Mark this as the multiple position in which to insert actual parameter values later @@ -584,8 +606,10 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul } /* Now perform class checks */ - if (elms->next == 0) /* No verb code, verb not declared! */ - error(M_CANT0); + if (elms->next == 0) { + /* No verb code, verb not declared! */ + CALL1(error, M_CANT0) + } for (p = 0; params[p].code != EOD; p++) /* Mark all parameters unchecked */ checked[p] = FALSE; @@ -613,8 +637,9 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul params[cla->code - 1].code = 0; } else { if (!claCheck(cla)) { + /* Return to player without saying anything */ interpret(cla->stms); - error(MSGMAX); /* Return to player without saying anything */ + CALL1(error, MSGMAX) } } checked[cla->code - 1] = TRUE; /* Remember that it's already checked */ @@ -628,8 +653,9 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul if (matchLst[i].code != 0) /* Skip any empty slots */ if (!isObj(matchLst[i].code)) matchLst[i].code = 0; - } else if (!isObj(params[p].code)) - error(M_CANT0); + } else if (!isObj(params[p].code)) { + CALL1(error, M_CANT0) + } } /* Set verb code */ @@ -649,28 +675,30 @@ static void tryMatch(ParamElem matchLst[] /* OUT - List of params allowed by mul compact(matchLst); if (lstlen(matchLst) == 0) { params[0].code = EOD; - error(M_WHAT_ALL); + CALL1(error, M_WHAT_ALL) } } else if (anyPlural) { compact(matchLst); if (lstlen(matchLst) == 0) /* If there where multiple parameters but non left, exit without a */ /* word, assuming we have already said enough */ - error(MSGMAX); + CALL1(error, MSGMAX) } plural = anyPlural; /* Remember that we found plural objects */ } /* OUT - List of params allowed by multiple */ -static void match(ParamElem *matchLst) { - tryMatch(matchLst); /* ... to understand what he said */ - if (wrds[wrdidx] != EOD && !isConj(wrds[wrdidx])) - error(M_WHAT); +static void match(CONTEXT, ParamElem *matchLst) { + /* ... to understand what he said */ + CALL1(tryMatch, matchLst) + if (wrds[wrdidx] != EOD && !isConj(wrds[wrdidx])) { + CALL1(error, M_WHAT) + } if (wrds[wrdidx] != EOD) /* More on this line? */ wrdidx++; /* If so skip the AND */ } -void parse() { +void parse(CONTEXT) { if (mlst == NULL) { /* Allocate large enough paramlists */ mlst = (ParamElem *) allocate(sizeof(ParamElem) * (MAXENTITY + 1)); mlst[0].code = EOD; @@ -682,7 +710,8 @@ void parse() { if (wrds[wrdidx] == EOD) { wrdidx = 0; - scan(); + CALL0(scan) + if (g_vm->shouldQuit()) return; } else if (anyOutput) @@ -698,12 +727,13 @@ void parse() { vrbwrd = wrds[wrdidx]; vrbcode = dict[vrbwrd].code; wrdidx++; - match(mlst); - action(mlst); /* mlst contains possible multiple params */ + CALL1(match, mlst) + /* mlst contains possible multiple params */ + CALL1(action, mlst) } else { params[0].code = EOD; pmlst[0].code = EOD; - nonverb(); + CALL0(nonverb) } } diff --git a/engines/glk/alan2/parse.h b/engines/glk/alan2/parse.h index af3276fd14..100d739aa7 100644 --- a/engines/glk/alan2/parse.h +++ b/engines/glk/alan2/parse.h @@ -25,6 +25,8 @@ /* Parse data for ALAN interpreter module. */ +#include "engines/glk/alan2/jumps.h" + namespace Glk { namespace Alan2 { @@ -40,7 +42,7 @@ extern int litCount; extern int vrbwrd; // Parse a new player command -extern void parse(void); +extern void parse(CONTEXT); } // End of namespace Alan2 } // End of namespace Glk -- cgit v1.2.3