~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to terps/glulxe/glkop.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* glkop.c: Glulxe code for Glk API dispatching.
 
2
    Designed by Andrew Plotkin <erkyrath@eblong.com>
 
3
    http://eblong.com/zarf/glulx/index.html
 
4
*/
 
5
 
 
6
/* This code is actually very general; it could work for almost any
 
7
   32-bit VM which remotely resembles Glulxe or the Z-machine in design.
 
8
   
 
9
   To be precise, we make the following assumptions:
 
10
 
 
11
   - An argument list is an array of 32-bit values, which can represent
 
12
     either integers or addresses.
 
13
   - We can read or write to a 32-bit integer in VM memory using the macros
 
14
     ReadMemory(addr) and WriteMemory(addr), where addr is an address
 
15
     taken from the argument list.
 
16
   - A character array is an actual array of bytes somewhere in terp
 
17
     memory, whose actual address can be computed by the macro
 
18
     AddressOfArray(addr). Again, addr is a VM address from the argument
 
19
     list.
 
20
   - An integer array is a sequence of integers somewhere in VM memory.
 
21
     The array can be turned into a C integer array by the macro
 
22
     CaptureIArray(addr, len), and released by ReleaseIArray().
 
23
     These macros are responsible for fixing byte-order and alignment
 
24
     (if the C ABI does not match the VM's). The passin, passout hints
 
25
     may be used to avoid unnecessary copying.
 
26
   - A Glk structure (such as event_t) is a set of integers somewhere
 
27
     in VM memory, which can be read and written with the macros
 
28
     ReadStructField(addr, fieldnum) and WriteStructField(addr, fieldnum).
 
29
     The fieldnum is an integer (from 0 to 3, for event_t.)
 
30
   - A VM string can be turned into a C-style string with the macro
 
31
     ptr = DecodeVMString(addr). After the string is used, this code
 
32
     calls ReleaseVMString(ptr), which should free any memory that
 
33
     DecodeVMString allocates.
 
34
   - A VM Unicode string can be turned into a zero-terminated array
 
35
     of 32-bit integers, in the same way, with DecodeVMUstring
 
36
     and ReleaseVMUstring.
 
37
 
 
38
     To work this code over for a new VM, just diddle the macros.
 
39
*/
 
40
 
 
41
#define ReadMemory(addr)  \
 
42
    (((addr) == 0xffffffff) \
 
43
      ? (stackptr -= 4, Stk4(stackptr)) \
 
44
      : (Mem4(addr)))
 
45
#define WriteMemory(addr, val)  \
 
46
    (((addr) == 0xffffffff) \
 
47
      ? (StkW4(stackptr, (val)), stackptr += 4) \
 
48
      : (MemW4((addr), (val))))
 
49
#define AddressOfArray(addr)  \
 
50
    (memmap + (addr))
 
51
#define CaptureIArray(addr, len, passin)  \
 
52
    (grab_temp_array(addr, len, passin))
 
53
#define ReleaseIArray(ptr, addr, len, passout)  \
 
54
    (release_temp_array(ptr, addr, len, passout))
 
55
#define ReadStructField(addr, fieldnum)  \
 
56
    (((addr) == 0xffffffff) \
 
57
      ? (stackptr -= 4, Stk4(stackptr)) \
 
58
      : (Mem4((addr)+(fieldnum)*4)))
 
59
#define WriteStructField(addr, fieldnum, val)  \
 
60
    (((addr) == 0xffffffff) \
 
61
      ? (StkW4(stackptr, (val)), stackptr += 4) \
 
62
      : (MemW4((addr)+(fieldnum)*4, (val))))
 
63
#define DecodeVMString(addr)  \
 
64
    (make_temp_string(addr))
 
65
#define ReleaseVMString(ptr)  \
 
66
    (free_temp_string(ptr))
 
67
#define DecodeVMUstring(addr)  \
 
68
    (make_temp_ustring(addr))
 
69
#define ReleaseVMUstring(ptr)  \
 
70
    (free_temp_ustring(ptr))
 
71
 
 
72
#include "glk.h"
 
73
#include "glulxe.h"
 
74
#include "gi_dispa.h"
 
75
 
 
76
typedef struct dispatch_splot_struct {
 
77
  int numwanted;
 
78
  int maxargs;
 
79
  gluniversal_t *garglist;
 
80
  glui32 *varglist;
 
81
  int numvargs;
 
82
  glui32 *retval;
 
83
} dispatch_splot_t;
 
84
 
 
85
/* We maintain a linked list of arrays being used for Glk calls. It is
 
86
   only used for integer (glui32) arrays -- char arrays are handled in
 
87
   place. It's not worth bothering with a hash table, since most
 
88
   arrays appear here only momentarily. */
 
89
 
 
90
typedef struct arrayref_struct arrayref_t;
 
91
struct arrayref_struct {
 
92
  void *array;
 
93
  glui32 addr;
 
94
  glui32 elemsize;
 
95
  glui32 len; /* elements */
 
96
  int retained;
 
97
  arrayref_t *next;
 
98
};
 
99
 
 
100
static arrayref_t *arrays = NULL;
 
101
 
 
102
/* We maintain a hash table for each opaque Glk class. classref_t are the
 
103
    nodes of the table, and classtable_t are the tables themselves. */
 
104
 
 
105
typedef struct classref_struct classref_t;
 
106
struct classref_struct {
 
107
  void *obj;
 
108
  glui32 id;
 
109
  int bucknum;
 
110
  classref_t *next;
 
111
};
 
112
 
 
113
#define CLASSHASH_SIZE (31)
 
114
typedef struct classtable_struct {
 
115
  glui32 lastid;
 
116
  classref_t *bucket[CLASSHASH_SIZE];
 
117
} classtable_t;
 
118
 
 
119
/* The list of hash tables, for the classes. */
 
120
static int num_classes = 0;
 
121
classtable_t **classes = NULL;
 
122
 
 
123
static classtable_t *new_classtable(glui32 firstid);
 
124
static void *classes_get(int classid, glui32 objid);
 
125
static classref_t *classes_put(int classid, void *obj);
 
126
static void classes_remove(int classid, void *obj);
 
127
 
 
128
static gidispatch_rock_t glulxe_classtable_register(void *obj, 
 
129
  glui32 objclass);
 
130
static void glulxe_classtable_unregister(void *obj, glui32 objclass, 
 
131
  gidispatch_rock_t objrock);
 
132
static gidispatch_rock_t glulxe_retained_register(void *array,
 
133
  glui32 len, char *typecode);
 
134
static void glulxe_retained_unregister(void *array, glui32 len, 
 
135
  char *typecode, gidispatch_rock_t objrock);
 
136
 
 
137
static glui32 *grab_temp_array(glui32 addr, glui32 len, int passin);
 
138
static void release_temp_array(glui32 *arr, glui32 addr, glui32 len, int passout);
 
139
 
 
140
static void prepare_glk_args(char *proto, dispatch_splot_t *splot);
 
141
static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth,
 
142
  int *argnumptr, glui32 subaddress, int subpassin);
 
143
static void unparse_glk_args(dispatch_splot_t *splot, char **proto, int depth,
 
144
  int *argnumptr, glui32 subaddress, int subpassout);
 
145
 
 
146
/* init_dispatch():
 
147
   Set up the class hash tables and other startup-time stuff. 
 
148
*/
 
149
int init_dispatch()
 
150
{
 
151
  int ix;
 
152
    
 
153
  /* Allocate the class hash tables. */
 
154
  num_classes = gidispatch_count_classes();
 
155
  classes = (classtable_t **)glulx_malloc(num_classes 
 
156
    * sizeof(classtable_t *));
 
157
  if (!classes)
 
158
    return FALSE;
 
159
    
 
160
  for (ix=0; ix<num_classes; ix++) {
 
161
    classes[ix] = new_classtable((glulx_random() % (glui32)(101)) + 1);
 
162
    if (!classes[ix])
 
163
      return FALSE;
 
164
  }
 
165
    
 
166
  /* Set up the two callbacks. */
 
167
  gidispatch_set_object_registry(&glulxe_classtable_register, 
 
168
    &glulxe_classtable_unregister);
 
169
  gidispatch_set_retained_registry(&glulxe_retained_register, 
 
170
    &glulxe_retained_unregister);
 
171
    
 
172
  return TRUE;
 
173
}
 
174
 
 
175
/* perform_glk():
 
176
   Turn a list of Glulx arguments into a list of Glk arguments,
 
177
   dispatch the function call, and return the result. 
 
178
*/
 
179
glui32 perform_glk(glui32 funcnum, glui32 numargs, glui32 *arglist)
 
180
{
 
181
  glui32 retval = 0;
 
182
 
 
183
  switch (funcnum) {
 
184
    /* To speed life up, we implement commonly-used Glk functions
 
185
       directly -- instead of bothering with the whole prototype 
 
186
       mess. */
 
187
 
 
188
  case 0x0080: /* put_char */
 
189
    if (numargs != 1)
 
190
      goto WrongArgNum;
 
191
    glk_put_char(arglist[0] & 0xFF);
 
192
    break;
 
193
  case 0x0081: /* put_char_stream */
 
194
    if (numargs != 2)
 
195
      goto WrongArgNum;
 
196
    glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
 
197
    break;
 
198
  case 0x00A0: /* char_to_lower */
 
199
    if (numargs != 1)
 
200
      goto WrongArgNum;
 
201
    retval = glk_char_to_lower(arglist[0] & 0xFF);
 
202
    break;
 
203
  case 0x00A1: /* char_to_upper */
 
204
    if (numargs != 1)
 
205
      goto WrongArgNum;
 
206
    retval = glk_char_to_upper(arglist[0] & 0xFF);
 
207
    break;
 
208
 
 
209
  WrongArgNum:
 
210
    fatal_error("Wrong number of arguments to Glk function.");
 
211
    break;
 
212
 
 
213
  default: {
 
214
    /* Go through the full dispatcher prototype foo. */
 
215
    char *proto, *cx;
 
216
    dispatch_splot_t splot;
 
217
    int argnum;
 
218
 
 
219
    /* Grab the string. */
 
220
    proto = gidispatch_prototype(funcnum);
 
221
    if (!proto)
 
222
      fatal_error("Unknown Glk function.");
 
223
 
 
224
    splot.varglist = arglist;
 
225
    splot.numvargs = numargs;
 
226
    splot.retval = &retval;
 
227
 
 
228
    /* The work goes in four phases. First, we figure out how many
 
229
       arguments we want, and allocate space for the Glk argument
 
230
       list. Then we go through the Glulxe arguments and load them 
 
231
       into the Glk list. Then we call. Then we go through the 
 
232
       arguments again, unloading the data back into Glulx memory. */
 
233
 
 
234
    /* Phase 0. */
 
235
    prepare_glk_args(proto, &splot);
 
236
 
 
237
    /* Phase 1. */
 
238
    argnum = 0;
 
239
    cx = proto;
 
240
    parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
 
241
 
 
242
    /* Phase 2. */
 
243
    gidispatch_call(funcnum, argnum, splot.garglist);
 
244
 
 
245
    /* Phase 3. */
 
246
    argnum = 0;
 
247
    cx = proto;
 
248
    unparse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
 
249
 
 
250
    break;
 
251
  }
 
252
  }
 
253
 
 
254
  return retval;
 
255
}
 
256
 
 
257
/* read_prefix():
 
258
   Read the prefixes of an argument string -- the "<>&+:#!" chars. 
 
259
*/
 
260
static char *read_prefix(char *cx, int *isref, int *isarray,
 
261
  int *passin, int *passout, int *nullok, int *isretained, 
 
262
  int *isreturn)
 
263
{
 
264
  *isref = FALSE;
 
265
  *passin = FALSE;
 
266
  *passout = FALSE;
 
267
  *nullok = TRUE;
 
268
  *isarray = FALSE;
 
269
  *isretained = FALSE;
 
270
  *isreturn = FALSE;
 
271
  while (1) {
 
272
    if (*cx == '<') {
 
273
      *isref = TRUE;
 
274
      *passout = TRUE;
 
275
    }
 
276
    else if (*cx == '>') {
 
277
      *isref = TRUE;
 
278
      *passin = TRUE;
 
279
    }
 
280
    else if (*cx == '&') {
 
281
      *isref = TRUE;
 
282
      *passout = TRUE;
 
283
      *passin = TRUE;
 
284
    }
 
285
    else if (*cx == '+') {
 
286
      *nullok = FALSE;
 
287
    }
 
288
    else if (*cx == ':') {
 
289
      *isref = TRUE;
 
290
      *passout = TRUE;
 
291
      *nullok = FALSE;
 
292
      *isreturn = TRUE;
 
293
    }
 
294
    else if (*cx == '#') {
 
295
      *isarray = TRUE;
 
296
    }
 
297
    else if (*cx == '!') {
 
298
      *isretained = TRUE;
 
299
    }
 
300
    else {
 
301
      break;
 
302
    }
 
303
    cx++;
 
304
  }
 
305
  return cx;
 
306
}
 
307
 
 
308
/* prepare_glk_args():
 
309
   This reads through the prototype string, and pulls Floo objects off the
 
310
   stack. It also works out the maximal number of gluniversal_t objects
 
311
   which could be used by the Glk call in question. It then allocates
 
312
   space for them.
 
313
*/
 
314
static void prepare_glk_args(char *proto, dispatch_splot_t *splot)
 
315
{
 
316
  static gluniversal_t *garglist = NULL;
 
317
  static int garglist_size = 0;
 
318
 
 
319
  int ix;
 
320
  int numwanted, numvargswanted, maxargs;
 
321
  char *cx;
 
322
 
 
323
  cx = proto;
 
324
  numwanted = 0;
 
325
  while (*cx >= '0' && *cx <= '9') {
 
326
    numwanted = 10 * numwanted + (*cx - '0');
 
327
    cx++;
 
328
  }
 
329
  splot->numwanted = numwanted;
 
330
 
 
331
  maxargs = 0; 
 
332
  numvargswanted = 0; 
 
333
  for (ix = 0; ix < numwanted; ix++) {
 
334
    int isref, passin, passout, nullok, isarray, isretained, isreturn;
 
335
    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
 
336
      &isretained, &isreturn);
 
337
    if (isref) {
 
338
      maxargs += 2;
 
339
    }
 
340
    else {
 
341
      maxargs += 1;
 
342
    }
 
343
    if (!isreturn) {
 
344
      if (isarray) {
 
345
        numvargswanted += 2;
 
346
      }
 
347
      else {
 
348
        numvargswanted += 1;
 
349
      }
 
350
    }
 
351
        
 
352
    if (*cx == 'I' || *cx == 'C') {
 
353
      cx += 2;
 
354
    }
 
355
    else if (*cx == 'Q') {
 
356
      cx += 2;
 
357
    }
 
358
    else if (*cx == 'S' || *cx == 'U') {
 
359
      cx += 1;
 
360
    }
 
361
    else if (*cx == '[') {
 
362
      int refdepth, nwx;
 
363
      cx++;
 
364
      nwx = 0;
 
365
      while (*cx >= '0' && *cx <= '9') {
 
366
        nwx = 10 * nwx + (*cx - '0');
 
367
        cx++;
 
368
      }
 
369
      maxargs += nwx; /* This is *only* correct because all structs contain
 
370
                         plain values. */
 
371
      refdepth = 1;
 
372
      while (refdepth > 0) {
 
373
        if (*cx == '[')
 
374
          refdepth++;
 
375
        else if (*cx == ']')
 
376
          refdepth--;
 
377
        cx++;
 
378
      }
 
379
    }
 
380
    else {
 
381
      fatal_error("Illegal format string.");
 
382
    }
 
383
  }
 
384
 
 
385
  if (*cx != ':' && *cx != '\0')
 
386
    fatal_error("Illegal format string.");
 
387
 
 
388
  splot->maxargs = maxargs;
 
389
 
 
390
  if (splot->numvargs != numvargswanted)
 
391
    fatal_error("Wrong number of arguments to Glk function.");
 
392
 
 
393
  if (garglist && garglist_size < maxargs) {
 
394
    glulx_free(garglist);
 
395
    garglist = NULL;
 
396
    garglist_size = 0;
 
397
  }
 
398
  if (!garglist) {
 
399
    garglist_size = maxargs + 16;
 
400
    garglist = (gluniversal_t *)glulx_malloc(garglist_size 
 
401
      * sizeof(gluniversal_t));
 
402
  }
 
403
  if (!garglist)
 
404
    fatal_error("Unable to allocate storage for Glk arguments.");
 
405
 
 
406
  splot->garglist = garglist;
 
407
}
 
408
 
 
409
/* parse_glk_args():
 
410
   This long and unpleasant function translates a set of Floo objects into
 
411
   a gluniversal_t array. It's recursive, too, to deal with structures.
 
412
*/
 
413
static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth,
 
414
  int *argnumptr, glui32 subaddress, int subpassin)
 
415
{
 
416
  char *cx;
 
417
  int ix, argx;
 
418
  int gargnum, numwanted;
 
419
  void *opref;
 
420
  gluniversal_t *garglist;
 
421
  glui32 *varglist;
 
422
  
 
423
  garglist = splot->garglist;
 
424
  varglist = splot->varglist;
 
425
  gargnum = *argnumptr;
 
426
  cx = *proto;
 
427
 
 
428
  numwanted = 0;
 
429
  while (*cx >= '0' && *cx <= '9') {
 
430
    numwanted = 10 * numwanted + (*cx - '0');
 
431
    cx++;
 
432
  }
 
433
 
 
434
  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
 
435
    char typeclass;
 
436
    int skipval;
 
437
    int isref, passin, passout, nullok, isarray, isretained, isreturn;
 
438
    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
 
439
      &isretained, &isreturn);
 
440
    
 
441
    typeclass = *cx;
 
442
    cx++;
 
443
 
 
444
    skipval = FALSE;
 
445
    if (isref) {
 
446
      if (!isreturn && varglist[ix] == 0) {
 
447
        if (!nullok)
 
448
          fatal_error("Zero passed invalidly to Glk function.");
 
449
        garglist[gargnum].ptrflag = FALSE;
 
450
        gargnum++;
 
451
        skipval = TRUE;
 
452
      }
 
453
      else {
 
454
        garglist[gargnum].ptrflag = TRUE;
 
455
        gargnum++;
 
456
      }
 
457
    }
 
458
    if (!skipval) {
 
459
      glui32 thisval;
 
460
 
 
461
      if (typeclass == '[') {
 
462
 
 
463
        parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin);
 
464
 
 
465
      }
 
466
      else if (isarray) {
 
467
        /* definitely isref */
 
468
 
 
469
        switch (typeclass) {
 
470
        case 'C':
 
471
          garglist[gargnum].array = AddressOfArray(varglist[ix]);
 
472
          gargnum++;
 
473
          ix++;
 
474
          garglist[gargnum].uint = varglist[ix];
 
475
          gargnum++;
 
476
          cx++;
 
477
          break;
 
478
        case 'I':
 
479
          garglist[gargnum].array = CaptureIArray(varglist[ix], varglist[ix+1], passin);
 
480
          gargnum++;
 
481
          ix++;
 
482
          garglist[gargnum].uint = varglist[ix];
 
483
          gargnum++;
 
484
          cx++;
 
485
          break;
 
486
        default:
 
487
          fatal_error("Illegal format string.");
 
488
          break;
 
489
        }
 
490
      }
 
491
      else {
 
492
        /* a plain value or a reference to one. */
 
493
 
 
494
        if (isreturn) {
 
495
          thisval = 0;
 
496
        }
 
497
        else if (depth > 0) {
 
498
          /* Definitely not isref or isarray. */
 
499
          if (subpassin)
 
500
            thisval = ReadStructField(subaddress, ix);
 
501
          else
 
502
            thisval = 0;
 
503
        }
 
504
        else if (isref) {
 
505
          if (passin)
 
506
            thisval = ReadMemory(varglist[ix]);
 
507
          else
 
508
            thisval = 0;
 
509
        }
 
510
        else {
 
511
          thisval = varglist[ix];
 
512
        }
 
513
 
 
514
        switch (typeclass) {
 
515
        case 'I':
 
516
          if (*cx == 'u')
 
517
            garglist[gargnum].uint = (glui32)(thisval);
 
518
          else if (*cx == 's')
 
519
            garglist[gargnum].sint = (glsi32)(thisval);
 
520
          else
 
521
            fatal_error("Illegal format string.");
 
522
          gargnum++;
 
523
          cx++;
 
524
          break;
 
525
        case 'Q':
 
526
          if (thisval) {
 
527
            opref = classes_get(*cx-'a', thisval);
 
528
            if (!opref) {
 
529
              fatal_error("Reference to nonexistent Glk object.");
 
530
            }
 
531
          }
 
532
          else {
 
533
            opref = NULL;
 
534
          }
 
535
          garglist[gargnum].opaqueref = opref;
 
536
          gargnum++;
 
537
          cx++;
 
538
          break;
 
539
        case 'C':
 
540
          if (*cx == 'u') 
 
541
            garglist[gargnum].uch = (unsigned char)(thisval);
 
542
          else if (*cx == 's')
 
543
            garglist[gargnum].sch = (signed char)(thisval);
 
544
          else if (*cx == 'n')
 
545
            garglist[gargnum].ch = (char)(thisval);
 
546
          else
 
547
            fatal_error("Illegal format string.");
 
548
          gargnum++;
 
549
          cx++;
 
550
          break;
 
551
        case 'S':
 
552
          garglist[gargnum].charstr = DecodeVMString(thisval);
 
553
          gargnum++;
 
554
          break;
 
555
#ifdef GLK_MODULE_UNICODE
 
556
        case 'U':
 
557
          garglist[gargnum].unicharstr = DecodeVMUstring(thisval);
 
558
          gargnum++;
 
559
          break;
 
560
#endif /* GLK_MODULE_UNICODE */
 
561
        default:
 
562
          fatal_error("Illegal format string.");
 
563
          break;
 
564
        }
 
565
      }
 
566
    }
 
567
    else {
 
568
      /* We got a null reference, so we have to skip the format element. */
 
569
      if (typeclass == '[') {
 
570
        int numsubwanted, refdepth;
 
571
        numsubwanted = 0;
 
572
        while (*cx >= '0' && *cx <= '9') {
 
573
          numsubwanted = 10 * numsubwanted + (*cx - '0');
 
574
          cx++;
 
575
        }
 
576
        refdepth = 1;
 
577
        while (refdepth > 0) {
 
578
          if (*cx == '[')
 
579
            refdepth++;
 
580
          else if (*cx == ']')
 
581
            refdepth--;
 
582
          cx++;
 
583
        }
 
584
      }
 
585
      else if (typeclass == 'S' || typeclass == 'U') {
 
586
        /* leave it */
 
587
      }
 
588
      else {
 
589
        cx++;
 
590
      }
 
591
    }    
 
592
  }
 
593
 
 
594
  if (depth > 0) {
 
595
    if (*cx != ']')
 
596
      fatal_error("Illegal format string.");
 
597
    cx++;
 
598
  }
 
599
  else {
 
600
    if (*cx != ':' && *cx != '\0')
 
601
      fatal_error("Illegal format string.");
 
602
  }
 
603
  
 
604
  *proto = cx;
 
605
  *argnumptr = gargnum;
 
606
}
 
607
 
 
608
/* unparse_glk_args():
 
609
   This is about the reverse of parse_glk_args(). 
 
610
*/
 
611
static void unparse_glk_args(dispatch_splot_t *splot, char **proto, int depth,
 
612
  int *argnumptr, glui32 subaddress, int subpassout)
 
613
{
 
614
  char *cx;
 
615
  int ix, argx;
 
616
  int gargnum, numwanted;
 
617
  void *opref;
 
618
  gluniversal_t *garglist;
 
619
  glui32 *varglist;
 
620
  
 
621
  garglist = splot->garglist;
 
622
  varglist = splot->varglist;
 
623
  gargnum = *argnumptr;
 
624
  cx = *proto;
 
625
 
 
626
  numwanted = 0;
 
627
  while (*cx >= '0' && *cx <= '9') {
 
628
    numwanted = 10 * numwanted + (*cx - '0');
 
629
    cx++;
 
630
  }
 
631
 
 
632
  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
 
633
    char typeclass;
 
634
    int skipval;
 
635
    int isref, passin, passout, nullok, isarray, isretained, isreturn;
 
636
    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
 
637
      &isretained, &isreturn);
 
638
    
 
639
    typeclass = *cx;
 
640
    cx++;
 
641
 
 
642
    skipval = FALSE;
 
643
    if (isref) {
 
644
      if (!isreturn && varglist[ix] == 0) {
 
645
        if (!nullok)
 
646
          fatal_error("Zero passed invalidly to Glk function.");
 
647
        garglist[gargnum].ptrflag = FALSE;
 
648
        gargnum++;
 
649
        skipval = TRUE;
 
650
      }
 
651
      else {
 
652
        garglist[gargnum].ptrflag = TRUE;
 
653
        gargnum++;
 
654
      }
 
655
    }
 
656
    if (!skipval) {
 
657
      glui32 thisval;
 
658
 
 
659
      if (typeclass == '[') {
 
660
 
 
661
        unparse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passout);
 
662
 
 
663
      }
 
664
      else if (isarray) {
 
665
        /* definitely isref */
 
666
 
 
667
        switch (typeclass) {
 
668
        case 'C':
 
669
          gargnum++;
 
670
          ix++;
 
671
          gargnum++;
 
672
          cx++;
 
673
          break;
 
674
        case 'I':
 
675
          ReleaseIArray(garglist[gargnum].array, varglist[ix], varglist[ix+1], passout);
 
676
          gargnum++;
 
677
          ix++;
 
678
          gargnum++;
 
679
          cx++;
 
680
          break;
 
681
        default:
 
682
          fatal_error("Illegal format string.");
 
683
          break;
 
684
        }
 
685
      }
 
686
      else {
 
687
        /* a plain value or a reference to one. */
 
688
 
 
689
        if (isreturn || (depth > 0 && subpassout) || (isref && passout)) {
 
690
          skipval = FALSE;
 
691
        }
 
692
        else {
 
693
          skipval = TRUE;
 
694
        }
 
695
 
 
696
        switch (typeclass) {
 
697
        case 'I':
 
698
          if (!skipval) {
 
699
            if (*cx == 'u')
 
700
              thisval = (glui32)garglist[gargnum].uint;
 
701
            else if (*cx == 's')
 
702
              thisval = (glui32)garglist[gargnum].sint;
 
703
            else
 
704
              fatal_error("Illegal format string.");
 
705
          }
 
706
          gargnum++;
 
707
          cx++;
 
708
          break;
 
709
        case 'Q':
 
710
          if (!skipval) {
 
711
            opref = garglist[gargnum].opaqueref;
 
712
            if (opref) {
 
713
              gidispatch_rock_t objrock = 
 
714
                gidispatch_get_objrock(opref, *cx-'a');
 
715
              thisval = ((classref_t *)objrock.ptr)->id;
 
716
            }
 
717
            else {
 
718
              thisval = 0;
 
719
            }
 
720
          }
 
721
          gargnum++;
 
722
          cx++;
 
723
          break;
 
724
        case 'C':
 
725
          if (!skipval) {
 
726
            if (*cx == 'u') 
 
727
              thisval = (glui32)garglist[gargnum].uch;
 
728
            else if (*cx == 's')
 
729
              thisval = (glui32)garglist[gargnum].sch;
 
730
            else if (*cx == 'n')
 
731
              thisval = (glui32)garglist[gargnum].ch;
 
732
            else
 
733
              fatal_error("Illegal format string.");
 
734
          }
 
735
          gargnum++;
 
736
          cx++;
 
737
          break;
 
738
        case 'S':
 
739
          if (garglist[gargnum].charstr)
 
740
            ReleaseVMString(garglist[gargnum].charstr);
 
741
          gargnum++;
 
742
          break;
 
743
#ifdef GLK_MODULE_UNICODE
 
744
        case 'U':
 
745
          if (garglist[gargnum].unicharstr)
 
746
            ReleaseVMUstring(garglist[gargnum].unicharstr);
 
747
          gargnum++;
 
748
          break;
 
749
#endif /* GLK_MODULE_UNICODE */
 
750
        default:
 
751
          fatal_error("Illegal format string.");
 
752
          break;
 
753
        }
 
754
 
 
755
        if (isreturn) {
 
756
          *(splot->retval) = thisval;
 
757
        }
 
758
        else if (depth > 0) {
 
759
          /* Definitely not isref or isarray. */
 
760
          if (subpassout)
 
761
            WriteStructField(subaddress, ix, thisval);
 
762
        }
 
763
        else if (isref) {
 
764
          if (passout)
 
765
            WriteMemory(varglist[ix], thisval); 
 
766
        }
 
767
      }
 
768
    }
 
769
    else {
 
770
      /* We got a null reference, so we have to skip the format element. */
 
771
      if (typeclass == '[') {
 
772
        int numsubwanted, refdepth;
 
773
        numsubwanted = 0;
 
774
        while (*cx >= '0' && *cx <= '9') {
 
775
          numsubwanted = 10 * numsubwanted + (*cx - '0');
 
776
          cx++;
 
777
        }
 
778
        refdepth = 1;
 
779
        while (refdepth > 0) {
 
780
          if (*cx == '[')
 
781
            refdepth++;
 
782
          else if (*cx == ']')
 
783
            refdepth--;
 
784
          cx++;
 
785
        }
 
786
      }
 
787
      else if (typeclass == 'S' || typeclass == 'U') {
 
788
        /* leave it */
 
789
      }
 
790
      else {
 
791
        cx++;
 
792
      }
 
793
    }    
 
794
  }
 
795
 
 
796
  if (depth > 0) {
 
797
    if (*cx != ']')
 
798
      fatal_error("Illegal format string.");
 
799
    cx++;
 
800
  }
 
801
  else {
 
802
    if (*cx != ':' && *cx != '\0')
 
803
      fatal_error("Illegal format string.");
 
804
  }
 
805
  
 
806
  *proto = cx;
 
807
  *argnumptr = gargnum;
 
808
}
 
809
 
 
810
/* find_stream_by_id():
 
811
   This is used by some interpreter code which has to, well, find a Glk
 
812
   stream given its ID. 
 
813
*/
 
814
strid_t find_stream_by_id(glui32 objid)
 
815
{
 
816
  if (!objid)
 
817
    return NULL;
 
818
 
 
819
  /* Recall that class 1 ("b") is streams. */
 
820
  return classes_get(1, objid);
 
821
}
 
822
 
 
823
/* Build a hash table to hold a set of Glk objects. */
 
824
static classtable_t *new_classtable(glui32 firstid)
 
825
{
 
826
  int ix;
 
827
  classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t));
 
828
  if (!ctab)
 
829
    return NULL;
 
830
    
 
831
  for (ix=0; ix<CLASSHASH_SIZE; ix++)
 
832
    ctab->bucket[ix] = NULL;
 
833
    
 
834
  ctab->lastid = firstid;
 
835
    
 
836
  return ctab;
 
837
}
 
838
 
 
839
/* Find a Glk object in the appropriate hash table. */
 
840
static void *classes_get(int classid, glui32 objid)
 
841
{
 
842
  classtable_t *ctab;
 
843
  classref_t *cref;
 
844
  if (classid < 0 || classid >= num_classes)
 
845
    return NULL;
 
846
  ctab = classes[classid];
 
847
  cref = ctab->bucket[objid % CLASSHASH_SIZE];
 
848
  for (; cref; cref = cref->next) {
 
849
    if (cref->id == objid)
 
850
      return cref->obj;
 
851
  }
 
852
  return NULL;
 
853
}
 
854
 
 
855
/* Put a Glk object in the appropriate hash table. */
 
856
static classref_t *classes_put(int classid, void *obj)
 
857
{
 
858
  int bucknum;
 
859
  classtable_t *ctab;
 
860
  classref_t *cref;
 
861
  if (classid < 0 || classid >= num_classes)
 
862
    return NULL;
 
863
  ctab = classes[classid];
 
864
  cref = (classref_t *)glulx_malloc(sizeof(classref_t));
 
865
  if (!cref)
 
866
    return NULL;
 
867
  cref->obj = obj;
 
868
  cref->id = ctab->lastid;
 
869
  ctab->lastid++;
 
870
  bucknum = cref->id % CLASSHASH_SIZE;
 
871
  cref->bucknum = bucknum;
 
872
  cref->next = ctab->bucket[bucknum];
 
873
  ctab->bucket[bucknum] = cref;
 
874
  return cref;
 
875
}
 
876
 
 
877
/* Delete a Glk object from the appropriate hash table. */
 
878
static void classes_remove(int classid, void *obj)
 
879
{
 
880
  classtable_t *ctab;
 
881
  classref_t *cref;
 
882
  classref_t **crefp;
 
883
  gidispatch_rock_t objrock;
 
884
  if (classid < 0 || classid >= num_classes)
 
885
    return;
 
886
  ctab = classes[classid];
 
887
  objrock = gidispatch_get_objrock(obj, classid);
 
888
  cref = objrock.ptr;
 
889
  if (!cref)
 
890
    return;
 
891
  crefp = &(ctab->bucket[cref->bucknum]);
 
892
  for (; *crefp; crefp = &((*crefp)->next)) {
 
893
    if ((*crefp) == cref) {
 
894
      *crefp = cref->next;
 
895
      if (!cref->obj) {
 
896
        nonfatal_warning("attempt to free NULL object!");
 
897
      }
 
898
      cref->obj = NULL;
 
899
      cref->id = 0;
 
900
      cref->next = NULL;
 
901
      glulx_free(cref);
 
902
      return;
 
903
    }
 
904
  }
 
905
  return;
 
906
}
 
907
 
 
908
/* The object registration/unregistration callbacks that the library calls
 
909
    to keep the hash tables up to date. */
 
910
    
 
911
static gidispatch_rock_t glulxe_classtable_register(void *obj, 
 
912
  glui32 objclass)
 
913
{
 
914
  classref_t *cref;
 
915
  gidispatch_rock_t objrock;
 
916
  cref = classes_put(objclass, obj);
 
917
  objrock.ptr = cref;
 
918
  return objrock;
 
919
}
 
920
 
 
921
static void glulxe_classtable_unregister(void *obj, glui32 objclass, 
 
922
  gidispatch_rock_t objrock)
 
923
{
 
924
  classes_remove(objclass, obj);
 
925
}
 
926
 
 
927
static glui32 *grab_temp_array(glui32 addr, glui32 len, int passin)
 
928
{
 
929
  arrayref_t *arref = NULL;
 
930
  glui32 *arr = NULL;
 
931
  glui32 ix, addr2;
 
932
 
 
933
  if (len) {
 
934
    arr = (glui32 *)glulx_malloc(len * sizeof(glui32));
 
935
    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
 
936
    if (!arr || !arref) 
 
937
      fatal_error("Unable to allocate space for array argument to Glk call.");
 
938
 
 
939
    arref->array = arr;
 
940
    arref->addr = addr;
 
941
    arref->elemsize = 4;
 
942
    arref->retained = FALSE;
 
943
    arref->len = len;
 
944
    arref->next = arrays;
 
945
    arrays = arref;
 
946
 
 
947
    if (passin) {
 
948
      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
 
949
        arr[ix] = Mem4(addr2);
 
950
      }
 
951
    }
 
952
  }
 
953
 
 
954
  return arr;
 
955
}
 
956
 
 
957
static void release_temp_array(glui32 *arr, glui32 addr, glui32 len, int passout)
 
958
{
 
959
  arrayref_t *arref = NULL;
 
960
  arrayref_t **aptr;
 
961
  glui32 ix, val, addr2;
 
962
 
 
963
  if (arr) {
 
964
    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
 
965
      if ((*aptr)->array == arr)
 
966
        break;
 
967
    }
 
968
    arref = *aptr;
 
969
    if (!arref)
 
970
      fatal_error("Unable to re-find array argument in Glk call.");
 
971
    if (arref->addr != addr || arref->len != len)
 
972
      fatal_error("Mismatched array argument in Glk call.");
 
973
 
 
974
    if (arref->retained) {
 
975
      return;
 
976
    }
 
977
 
 
978
    *aptr = arref->next;
 
979
    arref->next = NULL;
 
980
 
 
981
    if (passout) {
 
982
      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
 
983
        val = arr[ix];
 
984
        MemW4(addr2, val);
 
985
      }
 
986
    }
 
987
    glulx_free(arr);
 
988
    glulx_free(arref);
 
989
  }
 
990
}
 
991
 
 
992
gidispatch_rock_t glulxe_retained_register(void *array,
 
993
  glui32 len, char *typecode)
 
994
{
 
995
  gidispatch_rock_t rock;
 
996
  arrayref_t *arref = NULL;
 
997
  arrayref_t **aptr;
 
998
 
 
999
  if (typecode[4] != 'I' || array == NULL) {
 
1000
    /* We only retain integer arrays. */
 
1001
    rock.ptr = NULL;
 
1002
    return rock;
 
1003
  }
 
1004
 
 
1005
  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
 
1006
    if ((*aptr)->array == array)
 
1007
      break;
 
1008
  }
 
1009
  arref = *aptr;
 
1010
  if (!arref)
 
1011
    fatal_error("Unable to re-find array argument in Glk call.");
 
1012
  if (arref->elemsize != 4 || arref->len != len)
 
1013
    fatal_error("Mismatched array argument in Glk call.");
 
1014
 
 
1015
  arref->retained = TRUE;
 
1016
 
 
1017
  rock.ptr = arref;
 
1018
  return rock;
 
1019
}
 
1020
 
 
1021
void glulxe_retained_unregister(void *array, glui32 len, 
 
1022
  char *typecode, gidispatch_rock_t objrock)
 
1023
{
 
1024
  arrayref_t *arref = NULL;
 
1025
  arrayref_t **aptr;
 
1026
  glui32 ix, addr2, val;
 
1027
 
 
1028
  if (typecode[4] != 'I' || array == NULL) {
 
1029
    /* We only retain integer arrays. */
 
1030
    return;
 
1031
  }
 
1032
 
 
1033
  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
 
1034
    if ((*aptr)->array == array)
 
1035
      break;
 
1036
  }
 
1037
  arref = *aptr;
 
1038
  if (!arref)
 
1039
    fatal_error("Unable to re-find array argument in Glk call.");
 
1040
  if (arref != objrock.ptr)
 
1041
    fatal_error("Mismatched array reference in Glk call.");
 
1042
  if (!arref->retained)
 
1043
    fatal_error("Unretained array reference in Glk call.");
 
1044
  if (arref->elemsize != 4 || arref->len != len)
 
1045
    fatal_error("Mismatched array argument in Glk call.");
 
1046
 
 
1047
  *aptr = arref->next;
 
1048
  arref->next = NULL;
 
1049
 
 
1050
  for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=4) {
 
1051
    val = ((glui32 *)array)[ix];
 
1052
    MemW4(addr2, val);
 
1053
  }
 
1054
  glulx_free(array);
 
1055
  glulx_free(arref);
 
1056
}
 
1057