1 /* ScummVM - Graphic Adventure Engine 2 * 3 * ScummVM is the legal property of its developers, whose names 4 * are too numerous to list here. Please refer to the COPYRIGHT 5 * file distributed with this source distribution. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 /* Definitions for code execution 24 * 25 * The preprocessor symbol RUNFAST can be defined if run - time checking 26 * of stack overflow, stack underflow, and other unusual but potentially 27 * dangerous conditions is to be turned off.This will result in somewhat 28 * faster run-time performance, but run - time errors could be disastrous. 29 */ 30 31 #ifndef GLK_TADS_TADS2_RUN 32 #define GLK_TADS_TADS2_RUN 33 34 #include "common/scummsys.h" 35 #include "glk/tads/tads2/lib.h" 36 #include "glk/tads/tads2/debug.h" 37 #include "glk/tads/tads2/object.h" 38 #include "glk/tads/tads2/memory_cache.h" 39 #include "glk/tads/tads2/memory_cache_swap.h" 40 #include "glk/tads/tads2/opcode.h" 41 #include "glk/tads/tads2/property.h" 42 #include "glk/tads/tads2/text_io.h" 43 #include "glk/tads/tads2/tokenizer.h" 44 45 namespace Glk { 46 namespace TADS { 47 namespace TADS2 { 48 49 /* forward declarations */ 50 struct bifcxdef; 51 52 /* stack element - the stack is an array of these structures */ 53 struct runsdef { 54 uchar runstyp; /* type of element */ 55 union { 56 long runsvnum; /* numeric value */ 57 objnum runsvobj; /* object value */ 58 prpnum runsvprp; /* property number value */ 59 uchar *runsvstr; /* string/list value */ 60 } runsv; 61 runsdefrunsdef62 runsdef() : runstyp(0) { 63 runsv.runsvnum = 0; 64 } 65 }; 66 67 /* external function control structure */ 68 struct runxdef { 69 char runxnam[TOKNAMMAX + 1]; /* name of external function */ 70 int (*runxptr)(void *); /* pointer to memory containing code */ 71 }; 72 73 /* external function context structure - passed to user exits */ 74 struct runuxdef { 75 struct runcxdef osfar_t *runuxctx; /* run-time context */ 76 struct runufdef osfar_t *runuxvec; /* vector of functions */ 77 int runuxargc; /* count of arguments to function */ 78 }; 79 80 /* external function callback vector */ 81 struct runufdef { 82 int (osfar_t *runuftyp)(runuxdef *); /* type of top of stack */ 83 long (osfar_t *runufnpo)(runuxdef *); /* pop a number */ 84 uchar *(osfar_t *runufspo)(runuxdef *); /* pop a string */ 85 void (osfar_t *runufdsc)(runuxdef *); /* discard item at top of stack */ 86 void (osfar_t *runufnpu)(runuxdef *, long); /* push a number */ 87 void (osfar_t *runufspu)(runuxdef *, uchar *); /* push alloc'd string */ 88 void (osfar_t *runufcspu)(runuxdef *, char *); /* push a C-string */ 89 uchar *(osfar_t *runufsal)(runuxdef *, int); /* allocate a new string */ 90 void (osfar_t *runuflpu)(runuxdef *, int);/* push DAT_TRUE or DAT_NIL */ 91 }; 92 93 /* execution context */ 94 struct runcxdef { 95 errcxdef *runcxerr; /* error management context */ 96 mcmcxdef *runcxmem; /* cache manager context for object references */ 97 runsdef *runcxstk; /* base of interpreter stack */ 98 runsdef *runcxstop; /* top of stack */ 99 runsdef *runcxsp; /* current stack pointer (stack grows upwards) */ 100 runsdef *runcxbp; /* base pointer */ 101 uchar *runcxheap; /* run-time variable-length object heap */ 102 uchar *runcxhp; /* current heap pointer */ 103 uchar *runcxhtop; /* top of heap */ 104 objucxdef *runcxundo; /* undo context */ 105 tiocxdef *runcxtio; /* text I/O context */ 106 void *runcxbcx; /* context for built-in callback functions */ 107 void (**runcxbi)(struct bifcxdef *ctx, int argc); 108 /* built-in functions */ 109 struct dbgcxdef *runcxdbg; /* debugger context */ 110 struct voccxdef *runcxvoc; /* player command parser context */ 111 void (*runcxdmd)(void *ctx, objnum obj, prpnum prp); 112 /* demand-loader callback function */ 113 void *runcxdmc; /* demand-loader callback context */ 114 runxdef *runcxext; /* external function array */ 115 int runcxexc; /* count of external functions */ 116 uint runcxlofs; /* offset of last line record encountered */ 117 char *runcxgamename; /* name of the .GAM file */ 118 char *runcxgamepath; /* absolute directory path of .GAM file */ 119 }; 120 121 /* execute a function, given the function object number */ 122 void runfn(runcxdef *ctx, noreg objnum objn, int argc); 123 124 /* 125 * Execute p-code given a pointer to the code. p is the actual pointer 126 * to the first byte of code to be executed. self is the object to be 127 * used for the special 'self' pseudo-object, and target is the object 128 * whose data are actually being executed. targprop is the property being 129 * executed; 0 is used for functions. 130 */ 131 void runexe(runcxdef *ctx, uchar *p, objnum self, objnum target, 132 prpnum targprop, int argc); 133 134 /* push a value onto the stack */ 135 void runpush(runcxdef *ctx, dattyp typ, runsdef *val); 136 137 /* push a value onto the stack that's already in the heap */ 138 void runrepush(runcxdef *ctx, runsdef *val); 139 140 /* push a number onto the stack */ 141 void runpnum(runcxdef *ctx, long val); 142 143 /* push an object onto the stack */ 144 void runpobj(runcxdef *ctx, objnum obj); 145 146 /* push nil */ 147 void runpnil(runcxdef *ctx); 148 149 /* push a value onto the stack from a buffer (propdef, list) */ 150 void runpbuf(runcxdef *ctx, int typ, void *val); 151 152 /* push a counted-length string onto the stack */ 153 void runpstr(runcxdef *ctx, const char *str, int len, int sav); 154 155 /* 156 * Push a C-style string onto the stack, converting escape codes. If 157 * the character contains backslashes, newline, or tab characters, we'll 158 * convert these characters to their escaped equivalent. 159 */ 160 void runpushcstr(runcxdef *ctx, const char *str, size_t len, int sav); 161 162 /* 163 * Push a property onto the stack. codepp is a pointer to the caller's 164 * code pointer, which will be updated if necessary; callobj and 165 * callofsp are the object and starting offset within the object of the 166 * code being executed by the caller, which are needed to update 167 * *codepp. Property 0 is used if a function is being executed. obj 168 * and prop are the object and property number whose value is to be 169 * pushed. If 'inh' is TRUE, it means that only a property inherited 170 * by 'obj' is to be considered; this is used for "pass"/"inherited" 171 * operations, with the current target object given as 'obj'. 172 */ 173 void runpprop(runcxdef *ctx, uchar *noreg *codepp, objnum callobj, 174 prpnum callprop, noreg objnum obj, prpnum prop, int inh, 175 int argc, objnum self); 176 177 /* top level runpprop, when caller is not executing in an object */ 178 /* void runppr(runcxdef *ctx, objnum obj, prpnum prp, int argc); */ 179 #define runppr(ctx, obj, prp, argc) \ 180 runpprop(ctx, (uchar **)0, (objnum)0, (prpnum)0, obj, prp, FALSE, argc, obj) 181 182 /* discard top element on stack */ 183 /* void rundisc(runcxdef *ctx); */ 184 #define rundisc(ctx) (runstkund(ctx), (--((ctx)->runcxsp))) 185 186 /* pop the top element on the stack */ 187 /* void runpop(runcxdef *ctx, runsdef *val); */ 188 #define runpop(ctx, v) \ 189 (runstkund(ctx), memcpy(v, (--((ctx)->runcxsp)), (size_t)sizeof(runsdef))) 190 191 /* pop a numeric value, signalling an error if not a number */ 192 /* long runpopnum(runcxdef *ctx); */ 193 #define runpopnum(ctx) \ 194 (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_NUMBER ? \ 195 (runsig(ctx,ERR_REQNUM), (long)0) : \ 196 ((ctx)->runcxsp->runsv.runsvnum))) 197 198 /* pop an object, signalling an error if not an object */ 199 /* objnum runpopobj(runcxdef *ctx); */ 200 #define runpopobj(ctx) \ 201 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_OBJECT ? \ 202 (runsig(ctx,ERR_REQVOB), (objnum)0) : \ 203 ((ctx)->runcxsp->runsv.runsvobj)) 204 205 /* pop an object or nil - returns MCMONINV if the value is nil */ 206 #define runpopobjnil(ctx) \ 207 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp==DAT_OBJECT ? \ 208 ((ctx)->runcxsp->runsv.runsvobj) : \ 209 ((ctx)->runcxsp->runstyp==DAT_NIL ? MCMONINV : \ 210 (runsig(ctx,ERR_REQVOB), (objnum)0))) 211 212 /* pop a list, signalling an error if not a list */ 213 /* uchar *runpoplst(runcxdef *ctx); */ 214 #define runpoplst(ctx) \ 215 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_LIST ? \ 216 (runsig(ctx,ERR_REQVLS), (uchar *)0) : \ 217 (uchar *)((ctx)->runcxsp->runsv.runsvstr)) 218 219 /* pop a property number, signalling an error if not a property number */ 220 /* prpnum runpopprp(runcxdef *ctx); */ 221 #define runpopprp(ctx) \ 222 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_PROPNUM ? \ 223 (runsig(ctx,ERR_REQVPR), (prpnum)0) : \ 224 ((ctx)->runcxsp->runsv.runsvprp)) 225 226 227 /* pop function pointer */ 228 /* objnum runpopfn(runcxdef *ctx); */ 229 #define runpopfn(ctx) \ 230 ((objnum)(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_FNADDR ? \ 231 (runsig(ctx,ERR_REQVFN), (objnum)0) : \ 232 ((ctx)->runcxsp->runsv.runsvobj))) 233 234 /* pop a string value */ 235 /* char *runpopstr(runcxdef *ctx); */ 236 #define runpopstr(ctx) \ 237 (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_SSTRING ? \ 238 (runsig(ctx,ERR_REQSTR), (uchar *)0) : \ 239 ((ctx)->runcxsp->runsv.runsvstr))) 240 241 242 /* pop a logical value - TRUE for DAT_TRUE, FALSE for DAT_NIL */ 243 /* int runpoplog(runcxdef *ctx); */ 244 #define runpoplog(ctx) \ 245 ((--((ctx)->runcxsp))->runstyp==DAT_TRUE ? TRUE : \ 246 (ctx)->runcxsp->runstyp==DAT_NIL ? FALSE : \ 247 (runsig(ctx, ERR_REQLOG), 0)) 248 249 /* get type of top of stack */ 250 /* int runtostyp(runcxdef *ctx); */ 251 #define runtostyp(ctx) (((ctx)->runcxsp - 1)->runstyp) 252 253 /* determine if top of stack is logical value (returns TRUE if so) */ 254 /* int runtoslog(runcxdef *ctx); */ 255 #define runtoslog(ctx) \ 256 (runtostyp(ctx) == DAT_TRUE || runtostyp(ctx) == DAT_NIL) 257 258 /* convert C logical to TADS logical (TRUE->DAT_TRUE, FALSE->DAT_NIL) */ 259 /* int runclog(int log); */ 260 #define runclog(l) ((l) ? DAT_TRUE : DAT_NIL) 261 262 /* compare magnitudes of numbers/strings on top of stack; strcmp-like value */ 263 int runmcmp(runcxdef *ctx); 264 265 /* TRUE if items at top of stack are equal, FALSE otherwise */ 266 int runeq(runcxdef *ctx); 267 268 /* check for stack underflow */ 269 /* void runstkund(runcxdef *ctx); */ 270 271 /* check for stack overflow */ 272 /* void runstkovf(runcxdef *ctx); */ 273 274 /* 275 * Check to ensure we have enough arguments to pass to a function or method 276 * call - this simply ensures we have enough data in the current frame. 277 * This is important because the called function will be able to write to 278 * our frame. If we don't have enough arguments, we'll push enough 'nil' 279 * values to meet the need. 280 */ 281 #define runcheckargc(ctx, nargc) \ 282 while ((ctx)->runcxsp - (ctx)->runcxbp < *(nargc)) \ 283 runpnil(ctx) 284 285 #ifdef RUNFAST 286 # define runstkovf(ctx) (DISCARD 0) 287 # define runstkund(ctx) (DISCARD 0) 288 #else /* RUNFAST */ 289 # define runstkovf(ctx) \ 290 ((ctx)->runcxsp >= (ctx)->runcxstop ? (runsig(ctx, ERR_STKOVF), \ 291 DISCARD 0) : DISCARD 0) 292 # define runstkund(ctx) \ 293 ((ctx)->runcxsp == (ctx)->runcxstk ? runsig(ctx, ERR_STKUND), \ 294 DISCARD 0 : DISCARD 0) 295 #endif /* RUNFAST */ 296 297 /* reserve space in heap, collecting garbage if necessary */ 298 /* void runhres(runcxdef *ctx, uint siz, uint below); */ 299 #define runhres(ctx, siz, below) \ 300 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ 301 (runhcmp(ctx, siz, below, (runsdef *)0, (runsdef *)0, (runsdef *)0),\ 302 DISCARD 0)) 303 304 /* reserve space, with various amounts of saving */ 305 #define runhres1(ctx, siz, below, val1) \ 306 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ 307 (runhcmp(ctx, siz, below, val1, (runsdef *)0, (runsdef *)0), DISCARD 0)) 308 309 #define runhres2(ctx, siz, below, val1, val2) \ 310 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ 311 (runhcmp(ctx, siz, below, val1, val2, (runsdef *)0), DISCARD 0)) 312 313 #define runhres3(ctx, siz, below, val1, val2, val3) \ 314 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ 315 (runhcmp(ctx, siz, below, val1, val2, val3), DISCARD 0)) 316 317 /* garbage collect heap, making sure 'siz' bytes are available afterwards */ 318 void runhcmp(runcxdef *ctx, uint siz, uint below, 319 runsdef *val1, runsdef *val2, runsdef *val3); 320 321 /* determine size of a data item */ 322 int runsiz(runsdef *item); 323 324 /* find a sublist within a list, returning pointer to sublist or NULL */ 325 uchar *runfind(uchar *list, runsdef *item); 326 327 /* add two runsdef values, returning result in *val */ 328 void runadd(runcxdef *ctx, runsdef *val, runsdef *val2, uint below); 329 330 /* 331 * subtract val2 from val, returning result in *val; return TRUE if 332 * value changed, FALSE otherwise (this is returned when subtracting 333 * something from a list that isn't in the list) 334 */ 335 int runsub(runcxdef *ctx, runsdef *val, runsdef *val2, uint below); 336 337 /* restore code pointer from object.property + offset */ 338 uchar *runcprst(runcxdef *ctx, uint ofs, objnum obj, prpnum prop); 339 340 /* leave a stack frame, removing arguments */ 341 /* void runleave(runcxdef *ctx, uint parms); */ 342 #define runleave(ctx, parms) \ 343 (((ctx)->runcxsp = (ctx)->runcxbp), \ 344 ((ctx)->runcxbp = (runsdef *)((--((ctx)->runcxsp))->runsv.runsvstr)), \ 345 ((ctx)->runcxsp -= (parms))) 346 347 /* reset run-time: throw away entire stack and heap */ 348 /* void runrst(runcxdef *ctx); */ 349 #define runrst(ctx) (((ctx)->runcxsp = (ctx)->runcxstk), \ 350 ((ctx)->runcxhp = (ctx)->runcxheap), \ 351 dbgrst(ctx->runcxdbg)) 352 353 /* set up runtime status line display */ 354 void runistat(struct voccxdef *vctx, struct runcxdef *rctx, 355 struct tiocxdef *tctx); 356 357 /* signal a run-time error - allows debugger trapping */ 358 void runsign(runcxdef *ctx, int err); 359 360 /* sign a run-time error with zero arguments */ 361 #define runsig(ctx, err) (errargc((ctx)->runcxerr,0),runsign(ctx,err)) 362 363 /* signal a run-time error with one argument */ 364 #define runsig1(ctx, err, typ, arg) \ 365 (errargv((ctx)->runcxerr,0,typ,arg),errargc((ctx)->runcxerr,1),\ 366 runsign(ctx,err)) 367 368 /* draw status line */ 369 void runstat(); 370 371 /* initialize output status */ 372 void runistat(struct voccxdef *vctx, struct runcxdef *rctx, 373 struct tiocxdef *tctx); 374 375 } // End of namespace TADS2 376 } // End of namespace TADS 377 } // End of namespace Glk 378 379 #endif 380