aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/run.h
blob: 0a39fca4e2de7f50ffc463a5becdfeac94986b9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/* 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.
 *
 */

/* Definitions for code execution
 *
 * The preprocessor symbol RUNFAST can be defined if run - time checking
 * of stack overflow, stack underflow, and other unusual but potentially
 * dangerous conditions is to be turned off.This will result in somewhat
 * faster run-time performance, but run - time errors could be disastrous.
 */

#ifndef GLK_TADS_TADS2_RUN
#define GLK_TADS_TADS2_RUN

#include "common/scummsys.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/debug.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/memory_cache.h"
#include "glk/tads/tads2/memory_cache_swap.h"
#include "glk/tads/tads2/opcode.h"
#include "glk/tads/tads2/property.h"
#include "glk/tads/tads2/text_io.h"
#include "glk/tads/tads2/tokenizer.h"

namespace Glk {
namespace TADS {
namespace TADS2 {

/* forward declarations */
struct bifcxdef;

/* stack element - the stack is an array of these structures */
struct runsdef {
    uchar  runstyp;                                      /* type of element */
    union {
        long    runsvnum;                                  /* numeric value */
        objnum  runsvobj;                                   /* object value */
        prpnum  runsvprp;                          /* property number value */
        uchar  *runsvstr;                              /* string/list value */
    } runsv;
};

/* external function control structure */
struct runxdef {
    char    runxnam[TOKNAMMAX + 1];            /* name of external function */
    int   (*runxptr)(void *);          /* pointer to memory containing code */
};

/* external function context structure - passed to user exits */
struct runuxdef {
    struct runcxdef osfar_t *runuxctx;                  /* run-time context */
    struct runufdef osfar_t *runuxvec;               /* vector of functions */
    int                      runuxargc;   /* count of arguments to function */
};

/* external function callback vector */
struct runufdef {
    int    (osfar_t *runuftyp)(runuxdef *);         /* type of top of stack */
    long   (osfar_t *runufnpo)(runuxdef *);                 /* pop a number */
    uchar *(osfar_t *runufspo)(runuxdef *);                 /* pop a string */
    void   (osfar_t *runufdsc)(runuxdef *); /* discard item at top of stack */
    void   (osfar_t *runufnpu)(runuxdef *, long);          /* push a number */
    void   (osfar_t *runufspu)(runuxdef *, uchar *); /* push alloc'd string */
    void   (osfar_t *runufcspu)(runuxdef *, char *);     /* push a C-string */
    uchar *(osfar_t *runufsal)(runuxdef *, int);   /* allocate a new string */
    void   (osfar_t *runuflpu)(runuxdef *, int);/* push DAT_TRUE or DAT_NIL */
};

/* execution context */
struct runcxdef {
    errcxdef   *runcxerr;                       /* error management context */
    mcmcxdef   *runcxmem;    /* cache manager context for object references */
    runsdef    *runcxstk;                      /* base of interpreter stack */
    runsdef    *runcxstop;                                  /* top of stack */
    runsdef    *runcxsp;     /* current stack pointer (stack grows upwards) */
    runsdef    *runcxbp;                                    /* base pointer */
    uchar      *runcxheap;          /* run-time variable-length object heap */
    uchar      *runcxhp;                            /* current heap pointer */
    uchar      *runcxhtop;                                   /* top of heap */
    objucxdef  *runcxundo;                                  /* undo context */
    tiocxdef   *runcxtio;                               /* text I/O context */
    void       *runcxbcx;        /* context for built-in callback functions */
    void     (**runcxbi)(struct bifcxdef *ctx, int argc);
                                                      /* built-in functions */
    struct dbgcxdef *runcxdbg;                          /* debugger context */
    struct voccxdef *runcxvoc;             /* player command parser context */
    void      (*runcxdmd)(void *ctx, objnum obj, prpnum prp);
                                         /* demand-loader callback function */
    void       *runcxdmc;                 /* demand-loader callback context */
    runxdef    *runcxext;                        /* external function array */
    int         runcxexc;                    /* count of external functions */
    uint        runcxlofs;        /* offset of last line record encountered */
    char       *runcxgamename;                     /* name of the .GAM file */
    char       *runcxgamepath;      /* absolute directory path of .GAM file */
};

/* execute a function, given the function object number */
void runfn(runcxdef *ctx, noreg objnum objn, int argc);

/*
 *   Execute p-code given a pointer to the code.  p is the actual pointer
 *   to the first byte of code to be executed. self is the object to be
 *   used for the special 'self' pseudo-object, and target is the object
 *   whose data are actually being executed.  targprop is the property being
 *   executed; 0 is used for functions. 
 */
void runexe(runcxdef *ctx, uchar *p, objnum self, objnum target,
            prpnum targprop, int argc);

/* push a value onto the stack */
void runpush(runcxdef *ctx, dattyp typ, runsdef *val);

/* push a value onto the stack that's already in the heap */
void runrepush(runcxdef *ctx, runsdef *val);

/* push a number onto the stack */
void runpnum(runcxdef *ctx, long val);

/* push an object onto the stack */
void runpobj(runcxdef *ctx, objnum obj);

/* push nil */
void runpnil(runcxdef *ctx);

/* push a value onto the stack from a buffer (propdef, list) */
void runpbuf(runcxdef *ctx, int typ, void *val);

/* push a counted-length string onto the stack */
void runpstr(runcxdef *ctx, const char *str, int len, int sav);

/*
 *   Push a C-style string onto the stack, converting escape codes.  If
 *   the character contains backslashes, newline, or tab characters, we'll
 *   convert these characters to their escaped equivalent.
 */
void runpushcstr(runcxdef *ctx, const char *str, size_t len, int sav);

/*
 *   Push a property onto the stack.  codepp is a pointer to the caller's
 *   code pointer, which will be updated if necessary; callobj and
 *   callofsp are the object and starting offset within the object of the
 *   code being executed by the caller, which are needed to update
 *   *codepp.  Property 0 is used if a function is being executed.  obj
 *   and prop are the object and property number whose value is to be
 *   pushed.  If 'inh' is TRUE, it means that only a property inherited
 *   by 'obj' is to be considered; this is used for "pass"/"inherited"
 *   operations, with the current target object given as 'obj'.
 */
void runpprop(runcxdef *ctx, uchar *noreg *codepp, objnum callobj,
              prpnum callprop, noreg objnum obj, prpnum prop, int inh,
              int argc, objnum self);

/* top level runpprop, when caller is not executing in an object */
/* void runppr(runcxdef *ctx, objnum obj, prpnum prp, int argc); */
#define runppr(ctx, obj, prp, argc) \
 runpprop(ctx, (uchar **)0, (objnum)0, (prpnum)0, obj, prp, FALSE, argc, obj)

/* discard top element on stack */
/* void rundisc(runcxdef *ctx); */
#define rundisc(ctx) (runstkund(ctx), (--((ctx)->runcxsp)))

/* pop the top element on the stack */
/* void runpop(runcxdef *ctx, runsdef *val); */
#define runpop(ctx, v) \
 (runstkund(ctx), memcpy(v, (--((ctx)->runcxsp)), (size_t)sizeof(runsdef)))

/* pop a numeric value, signalling an error if not a number */
/* long runpopnum(runcxdef *ctx); */
#define runpopnum(ctx) \
 (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_NUMBER ? \
  (runsig(ctx,ERR_REQNUM), (long)0) : \
  ((ctx)->runcxsp->runsv.runsvnum)))

/* pop an object, signalling an error if not an object */
/* objnum runpopobj(runcxdef *ctx); */
#define runpopobj(ctx) \
 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_OBJECT ? \
  (runsig(ctx,ERR_REQVOB), (objnum)0) : \
  ((ctx)->runcxsp->runsv.runsvobj))

/* pop an object or nil - returns MCMONINV if the value is nil */
#define runpopobjnil(ctx) \
  (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp==DAT_OBJECT ? \
   ((ctx)->runcxsp->runsv.runsvobj) : \
   ((ctx)->runcxsp->runstyp==DAT_NIL ? MCMONINV : \
    (runsig(ctx,ERR_REQVOB), (objnum)0)))

/* pop a list, signalling an error if not a list */
/* uchar *runpoplst(runcxdef *ctx); */
#define runpoplst(ctx) \
 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_LIST ? \
  (runsig(ctx,ERR_REQVLS), (uchar *)0) : \
  (uchar *)((ctx)->runcxsp->runsv.runsvstr))

/* pop a property number, signalling an error if not a property number */
/* prpnum runpopprp(runcxdef *ctx); */
#define runpopprp(ctx) \
 (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_PROPNUM ? \
  (runsig(ctx,ERR_REQVPR), (prpnum)0) : \
  ((ctx)->runcxsp->runsv.runsvprp))


/* pop function pointer */
/* objnum runpopfn(runcxdef *ctx); */
#define runpopfn(ctx) \
  ((objnum)(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_FNADDR ? \
                      (runsig(ctx,ERR_REQVFN), (objnum)0) : \
                      ((ctx)->runcxsp->runsv.runsvobj)))

/* pop a string value */
/* char *runpopstr(runcxdef *ctx); */
#define runpopstr(ctx) \
 (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_SSTRING ? \
  (runsig(ctx,ERR_REQSTR), (uchar *)0) : \
  ((ctx)->runcxsp->runsv.runsvstr)))


/* pop a logical value - TRUE for DAT_TRUE, FALSE for DAT_NIL */
/* int runpoplog(runcxdef *ctx); */
#define runpoplog(ctx) \
 ((--((ctx)->runcxsp))->runstyp==DAT_TRUE ? TRUE : \
  (ctx)->runcxsp->runstyp==DAT_NIL ? FALSE : \
  (runsig(ctx, ERR_REQLOG), 0))

/* get type of top of stack */
/* int runtostyp(runcxdef *ctx); */
#define runtostyp(ctx) (((ctx)->runcxsp - 1)->runstyp)

/* determine if top of stack is logical value (returns TRUE if so) */
/* int runtoslog(runcxdef *ctx); */
#define runtoslog(ctx) \
 (runtostyp(ctx) == DAT_TRUE || runtostyp(ctx) == DAT_NIL)

/* convert C logical to TADS logical (TRUE->DAT_TRUE, FALSE->DAT_NIL) */
/* int runclog(int log); */
#define runclog(l) ((l) ? DAT_TRUE : DAT_NIL)

/* compare magnitudes of numbers/strings on top of stack; strcmp-like value */
int runmcmp(runcxdef *ctx);

/* TRUE if items at top of stack are equal, FALSE otherwise */
int runeq(runcxdef *ctx);

/* check for stack underflow */
/* void runstkund(runcxdef *ctx); */

/* check for stack overflow */
/* void runstkovf(runcxdef *ctx); */

/* 
 *   Check to ensure we have enough arguments to pass to a function or method
 *   call - this simply ensures we have enough data in the current frame.
 *   This is important because the called function will be able to write to
 *   our frame.  If we don't have enough arguments, we'll push enough 'nil'
 *   values to meet the need.  
 */
#define runcheckargc(ctx, nargc) \
    while ((ctx)->runcxsp - (ctx)->runcxbp < *(nargc)) \
        runpnil(ctx)

#ifdef RUNFAST
# define runstkovf(ctx) (DISCARD 0)
# define runstkund(ctx) (DISCARD 0)
#else /* RUNFAST */
# define runstkovf(ctx) \
 ((ctx)->runcxsp >= (ctx)->runcxstop ? (runsig(ctx, ERR_STKOVF), \
 DISCARD 0) : DISCARD 0)
# define runstkund(ctx) \
 ((ctx)->runcxsp == (ctx)->runcxstk ? runsig(ctx, ERR_STKUND), \
 DISCARD 0 : DISCARD 0)
#endif /* RUNFAST */

/* reserve space in heap, collecting garbage if necessary */
/* void runhres(runcxdef *ctx, uint siz, uint below); */
#define runhres(ctx, siz, below) \
 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
 (runhcmp(ctx, siz, below, (runsdef *)0, (runsdef *)0, (runsdef *)0),\
  DISCARD 0))

/* reserve space, with various amounts of saving */
#define runhres1(ctx, siz, below, val1) \
  ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
  (runhcmp(ctx, siz, below, val1, (runsdef *)0, (runsdef *)0), DISCARD 0))

#define runhres2(ctx, siz, below, val1, val2) \
 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
 (runhcmp(ctx, siz, below, val1, val2, (runsdef *)0), DISCARD 0))

#define runhres3(ctx, siz, below, val1, val2, val3) \
 ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
 (runhcmp(ctx, siz, below, val1, val2, val3), DISCARD 0))

/* garbage collect heap, making sure 'siz' bytes are available afterwards */
void runhcmp(runcxdef *ctx, uint siz, uint below,
             runsdef *val1, runsdef *val2, runsdef *val3);

/* determine size of a data item */
int runsiz(runsdef *item);

/* find a sublist within a list, returning pointer to sublist or NULL */
uchar *runfind(uchar *list, runsdef *item);

/* add two runsdef values, returning result in *val */
void runadd(runcxdef *ctx, runsdef *val, runsdef *val2, uint below);

/*
 *   subtract val2 from val, returning result in *val; return TRUE if
 *   value changed, FALSE otherwise (this is returned when subtracting
 *   something from a list that isn't in the list) 
 */
int runsub(runcxdef *ctx, runsdef *val, runsdef *val2, uint below);

/* restore code pointer from object.property + offset */
uchar *runcprst(runcxdef *ctx, uint ofs, objnum obj, prpnum prop);

/* leave a stack frame, removing arguments */
/* void runleave(runcxdef *ctx, uint parms); */
#define runleave(ctx, parms) \
 (((ctx)->runcxsp = (ctx)->runcxbp), \
  ((ctx)->runcxbp = (runsdef *)((--((ctx)->runcxsp))->runsv.runsvstr)), \
  ((ctx)->runcxsp -= (parms)))

/* reset run-time: throw away entire stack and heap */
/* void runrst(runcxdef *ctx); */
#define runrst(ctx) (((ctx)->runcxsp = (ctx)->runcxstk), \
                     ((ctx)->runcxhp = (ctx)->runcxheap), \
                     dbgrst(ctx->runcxdbg))

/* set up runtime status line display */
void runistat(struct voccxdef *vctx, struct runcxdef *rctx,
                  struct tiocxdef *tctx);

/* signal a run-time error - allows debugger trapping */
void runsign(runcxdef *ctx, int err);

/* sign a run-time error with zero arguments */
#define runsig(ctx, err) (errargc((ctx)->runcxerr,0),runsign(ctx,err))

/* signal a run-time error with one argument */
#define runsig1(ctx, err, typ, arg) \
  (errargv((ctx)->runcxerr,0,typ,arg),errargc((ctx)->runcxerr,1),\
   runsign(ctx,err))

/* draw status line */
void runstat();

/* initialize output status */
void runistat(struct voccxdef *vctx, struct runcxdef *rctx,
              struct tiocxdef *tctx);

} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

#endif