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