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