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

« back to all changes in this revision

Viewing changes to terps/glulxe/string.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
/* string.c: Glulxe string and text functions.
 
2
    Designed by Andrew Plotkin <erkyrath@eblong.com>
 
3
    http://eblong.com/zarf/glulx/index.html
 
4
*/
 
5
 
 
6
#include "glk.h"
 
7
#include "glulxe.h"
 
8
 
 
9
static glui32 iosys_mode;
 
10
static glui32 iosys_rock;
 
11
/* These constants are defined in the Glulx spec. */
 
12
#define iosys_None (0)
 
13
#define iosys_Filter (1)
 
14
#define iosys_Glk (2)
 
15
 
 
16
#define CACHEBITS (4)
 
17
#define CACHESIZE (1<<CACHEBITS) 
 
18
#define CACHEMASK (15)
 
19
 
 
20
typedef struct cacheblock_struct {
 
21
  int depth; /* 1 to 4 */
 
22
  int type;
 
23
  union {
 
24
    struct cacheblock_struct *branches;
 
25
    unsigned char ch;
 
26
    glui32 uch;
 
27
    glui32 addr;
 
28
  } u;
 
29
} cacheblock_t;
 
30
 
 
31
static int never_cache_stringtable = FALSE;
 
32
 
 
33
/* The current string-decoding tables, broken out into a fast and
 
34
   easy-to-use form. */
 
35
static int tablecache_valid = FALSE;
 
36
static cacheblock_t tablecache;
 
37
 
 
38
static void stream_setup_unichar(void);
 
39
 
 
40
static void nopio_char_han(unsigned char ch);
 
41
static void filio_char_han(unsigned char ch);
 
42
static void nopio_unichar_han(glui32 ch);
 
43
static void filio_unichar_han(glui32 ch);
 
44
static void glkio_unichar_nouni_han(glui32 val);
 
45
static void (*glkio_unichar_han_ptr)(glui32 val) = NULL;
 
46
 
 
47
static void dropcache(cacheblock_t *cablist);
 
48
static void buildcache(cacheblock_t *cablist, glui32 nodeaddr, int depth,
 
49
  int mask);
 
50
static void dumpcache(cacheblock_t *cablist, int count, int indent);
 
51
 
 
52
void stream_get_iosys(glui32 *mode, glui32 *rock)
 
53
{
 
54
  *mode = iosys_mode;
 
55
  *rock = iosys_rock;
 
56
}
 
57
 
 
58
static void stream_setup_unichar()
 
59
{
 
60
#ifdef GLK_MODULE_UNICODE
 
61
 
 
62
  if (glk_gestalt(gestalt_Unicode, 0))
 
63
    glkio_unichar_han_ptr = glk_put_char_uni;
 
64
  else
 
65
    glkio_unichar_han_ptr = glkio_unichar_nouni_han;
 
66
 
 
67
#else /* GLK_MODULE_UNICODE */
 
68
 
 
69
  glkio_unichar_han_ptr = glkio_unichar_nouni_han;
 
70
 
 
71
#endif /* GLK_MODULE_UNICODE */
 
72
}
 
73
 
 
74
void stream_set_iosys(glui32 mode, glui32 rock)
 
75
{
 
76
  switch (mode) {
 
77
  default:
 
78
    mode = 0;
 
79
    /* ...and fall through to next case (no-op I/O). */
 
80
  case iosys_None:
 
81
    rock = 0;
 
82
    stream_char_handler = nopio_char_han;
 
83
    stream_unichar_handler = nopio_unichar_han;
 
84
    break;
 
85
  case iosys_Filter:
 
86
    stream_char_handler = filio_char_han;
 
87
    stream_unichar_handler = filio_unichar_han;
 
88
    break;
 
89
  case iosys_Glk:
 
90
    if (!glkio_unichar_han_ptr)
 
91
      stream_setup_unichar();
 
92
    rock = 0;
 
93
    stream_char_handler = glk_put_char;
 
94
    stream_unichar_handler = glkio_unichar_han_ptr;
 
95
    break;
 
96
  }
 
97
 
 
98
  iosys_mode = mode;
 
99
  iosys_rock = rock;
 
100
}
 
101
 
 
102
static void nopio_char_han(unsigned char ch)
 
103
{
 
104
}
 
105
 
 
106
static void nopio_unichar_han(glui32 ch)
 
107
{
 
108
}
 
109
 
 
110
static void filio_char_han(unsigned char ch)
 
111
{
 
112
  glui32 val = ch;
 
113
  push_callstub(0, 0);
 
114
  enter_function(iosys_rock, 1, &val);
 
115
}
 
116
 
 
117
static void filio_unichar_han(glui32 val)
 
118
{
 
119
  push_callstub(0, 0);
 
120
  enter_function(iosys_rock, 1, &val);
 
121
}
 
122
 
 
123
static void glkio_unichar_nouni_han(glui32 val)
 
124
{
 
125
  /* Only used if the Glk library has no Unicode functions */
 
126
  if (val > 0xFF)
 
127
    val = '?';
 
128
  glk_put_char(val);
 
129
}
 
130
 
 
131
/* stream_num():
 
132
   Write a signed integer to the current output stream.
 
133
*/
 
134
void stream_num(glsi32 val, int inmiddle, int charnum)
 
135
{
 
136
  int ix = 0;
 
137
  int res, jx;
 
138
  char buf[16];
 
139
  glui32 ival;
 
140
 
 
141
  if (val == 0) {
 
142
    buf[ix] = '0';
 
143
    ix++;
 
144
  }
 
145
  else {
 
146
    if (val < 0) 
 
147
      ival = -val;
 
148
    else 
 
149
      ival = val;
 
150
 
 
151
    while (ival != 0) {
 
152
      buf[ix] = (ival % 10) + '0';
 
153
      ix++;
 
154
      ival /= 10;
 
155
    }
 
156
 
 
157
    if (val < 0) {
 
158
      buf[ix] = '-';
 
159
      ix++;
 
160
    }
 
161
  }
 
162
 
 
163
  switch (iosys_mode) {
 
164
 
 
165
  case iosys_Glk:
 
166
    while (ix) {
 
167
      ix--;
 
168
      glk_put_char(buf[ix]);
 
169
    }
 
170
    break;
 
171
 
 
172
  case iosys_Filter:
 
173
    if (!inmiddle) {
 
174
      push_callstub(0x11, 0);
 
175
    }
 
176
    if (charnum >= ix) {
 
177
      res = pop_callstub_string(&jx);
 
178
      if (res) 
 
179
        fatal_error("String-on-string call stub while printing number.");
 
180
    }
 
181
    else {
 
182
      ival = buf[(ix-1)-charnum] & 0xFF;
 
183
      pc = val;
 
184
      push_callstub(0x12, charnum+1);
 
185
      enter_function(iosys_rock, 1, &ival);
 
186
    }
 
187
    break;
 
188
 
 
189
  default:
 
190
    break;
 
191
 
 
192
  }
 
193
}
 
194
 
 
195
/* stream_string():
 
196
   Write a Glulx string object to the current output stream.
 
197
   inmiddle is zero if we are beginning a new string, or
 
198
   nonzero if restarting one (E0/E1/E2, as appropriate for
 
199
   the string type).
 
200
*/
 
201
void stream_string(glui32 addr, int inmiddle, int bitnum)
 
202
{
 
203
  int ch;
 
204
  int type;
 
205
  int alldone = FALSE;
 
206
  int substring = (inmiddle != 0);
 
207
  glui32 ival;
 
208
 
 
209
  if (!addr)
 
210
    fatal_error("Called stream_string with null address.");
 
211
  
 
212
  while (!alldone) {
 
213
 
 
214
    if (inmiddle == 0) {
 
215
      type = Mem1(addr);
 
216
      if (type == 0xE2)
 
217
        addr+=4;
 
218
      else
 
219
        addr++;
 
220
      bitnum = 0;
 
221
    }
 
222
    else {
 
223
      type = inmiddle;
 
224
    }
 
225
 
 
226
    if (type == 0xE1) {
 
227
      if (tablecache_valid) {
 
228
        int bits, numbits;
 
229
        int readahead;
 
230
        glui32 tmpaddr;
 
231
        cacheblock_t *cablist;
 
232
        int done = 0;
 
233
 
 
234
        /* bitnum is already set right */
 
235
        bits = Mem1(addr); 
 
236
        if (bitnum)
 
237
          bits >>= bitnum;
 
238
        numbits = (8 - bitnum);
 
239
        readahead = FALSE;
 
240
 
 
241
        if (tablecache.type != 0) {
 
242
          /* This is a bit of a cheat. If the top-level block is not
 
243
             a branch, then it must be a string-terminator -- otherwise
 
244
             the string would be an infinite repetition of that block.
 
245
             We check for this case and bail immediately. */
 
246
          done = 1;
 
247
        }
 
248
 
 
249
        cablist = tablecache.u.branches;
 
250
        while (!done) {
 
251
          cacheblock_t *cab;
 
252
 
 
253
          if (numbits < CACHEBITS) {
 
254
            /* readahead is certainly false */
 
255
            int newbyte = Mem1(addr+1);
 
256
            bits |= (newbyte << numbits);
 
257
            numbits += 8;
 
258
            readahead = TRUE;
 
259
          }
 
260
 
 
261
          cab = &(cablist[bits & CACHEMASK]);
 
262
          numbits -= cab->depth;
 
263
          bits >>= cab->depth;
 
264
          bitnum += cab->depth;
 
265
          if (bitnum >= 8) {
 
266
            addr += 1;
 
267
            bitnum -= 8;
 
268
            if (readahead) {
 
269
              readahead = FALSE;
 
270
            }
 
271
            else {
 
272
              int newbyte = Mem1(addr);
 
273
              bits |= (newbyte << numbits);
 
274
              numbits += 8;
 
275
            }
 
276
          }
 
277
 
 
278
          switch (cab->type) {
 
279
          case 0x00: /* non-leaf node */
 
280
            cablist = cab->u.branches;
 
281
            break;
 
282
          case 0x01: /* string terminator */
 
283
            done = 1;
 
284
            break;
 
285
          case 0x02: /* single character */
 
286
            switch (iosys_mode) {
 
287
            case iosys_Glk:
 
288
              glk_put_char(cab->u.ch);
 
289
              break;
 
290
            case iosys_Filter: 
 
291
              ival = cab->u.ch & 0xFF;
 
292
              if (!substring) {
 
293
                push_callstub(0x11, 0);
 
294
                substring = TRUE;
 
295
              }
 
296
              pc = addr;
 
297
              push_callstub(0x10, bitnum);
 
298
              enter_function(iosys_rock, 1, &ival);
 
299
              return;
 
300
            }
 
301
            cablist = tablecache.u.branches;
 
302
            break;
 
303
          case 0x04: /* single Unicode character */
 
304
            switch (iosys_mode) {
 
305
            case iosys_Glk:
 
306
              glkio_unichar_han_ptr(cab->u.uch);
 
307
              break;
 
308
            case iosys_Filter: 
 
309
              ival = cab->u.uch;
 
310
              if (!substring) {
 
311
                push_callstub(0x11, 0);
 
312
                substring = TRUE;
 
313
              }
 
314
              pc = addr;
 
315
              push_callstub(0x10, bitnum);
 
316
              enter_function(iosys_rock, 1, &ival);
 
317
              return;
 
318
            }
 
319
            cablist = tablecache.u.branches;
 
320
            break;
 
321
          case 0x03: /* C string */
 
322
            switch (iosys_mode) {
 
323
            case iosys_Glk:
 
324
              for (tmpaddr=cab->u.addr; (ch=Mem1(tmpaddr)) != '\0'; tmpaddr++) 
 
325
                glk_put_char(ch);
 
326
              cablist = tablecache.u.branches; 
 
327
              break;
 
328
            case iosys_Filter:
 
329
              if (!substring) {
 
330
                push_callstub(0x11, 0);
 
331
                substring = TRUE;
 
332
              }
 
333
              pc = addr;
 
334
              push_callstub(0x10, bitnum);
 
335
              inmiddle = 0xE0;
 
336
              addr = cab->u.addr;
 
337
              done = 2;
 
338
              break;
 
339
            default:
 
340
              cablist = tablecache.u.branches; 
 
341
              break;
 
342
            }
 
343
            break;
 
344
          case 0x05: /* C Unicode string */
 
345
            switch (iosys_mode) {
 
346
            case iosys_Glk:
 
347
              for (tmpaddr=cab->u.addr; (ival=Mem4(tmpaddr)) != 0; tmpaddr+=4) 
 
348
                glkio_unichar_han_ptr(ival);
 
349
              cablist = tablecache.u.branches; 
 
350
              break;
 
351
            case iosys_Filter:
 
352
              if (!substring) {
 
353
                push_callstub(0x11, 0);
 
354
                substring = TRUE;
 
355
              }
 
356
              pc = addr;
 
357
              push_callstub(0x10, bitnum);
 
358
              inmiddle = 0xE2;
 
359
              addr = cab->u.addr;
 
360
              done = 2;
 
361
              break;
 
362
            default:
 
363
              cablist = tablecache.u.branches; 
 
364
              break;
 
365
            }
 
366
            break;
 
367
          case 0x08:
 
368
          case 0x09:
 
369
          case 0x0A:
 
370
          case 0x0B: 
 
371
            {
 
372
              glui32 oaddr;
 
373
              int otype;
 
374
              oaddr = cab->u.addr;
 
375
              if (cab->type >= 0x09)
 
376
                oaddr = Mem4(oaddr);
 
377
              if (cab->type == 0x0B)
 
378
                oaddr = Mem4(oaddr);
 
379
              otype = Mem1(oaddr);
 
380
              if (!substring) {
 
381
                push_callstub(0x11, 0);
 
382
                substring = TRUE;
 
383
              }
 
384
              if (otype >= 0xE0 && otype <= 0xFF) {
 
385
                pc = addr;
 
386
                push_callstub(0x10, bitnum);
 
387
                inmiddle = 0;
 
388
                addr = oaddr;
 
389
                done = 2;
 
390
              }
 
391
              else if (otype >= 0xC0 && otype <= 0xDF) {
 
392
                glui32 argc;
 
393
                glui32 *argv;
 
394
                if (cab->type == 0x0A || cab->type == 0x0B) {
 
395
                  argc = Mem4(cab->u.addr+4);
 
396
                  argv = pop_arguments(argc, cab->u.addr+8);
 
397
                }
 
398
                else {
 
399
                  argc = 0;
 
400
                  argv = NULL;
 
401
                }
 
402
                pc = addr;
 
403
                push_callstub(0x10, bitnum);
 
404
                enter_function(oaddr, argc, argv);
 
405
                return;
 
406
              }
 
407
              else {
 
408
                fatal_error("Unknown object while decoding string indirect reference.");
 
409
              }
 
410
            }
 
411
            break;
 
412
          default:
 
413
            fatal_error("Unknown entity in string decoding (cached).");
 
414
            break;
 
415
          }
 
416
        }
 
417
        if (done > 1) {
 
418
          continue; /* restart the top-level loop */
 
419
        }
 
420
      }
 
421
      else { /* tablecache not valid */
 
422
        glui32 node;
 
423
        int byte;
 
424
        int nodetype;
 
425
        int done = 0;
 
426
 
 
427
        if (!stringtable)
 
428
          fatal_error("Attempted to print a compressed string with no table set.");
 
429
        /* bitnum is already set right */
 
430
        byte = Mem1(addr); 
 
431
        if (bitnum)
 
432
          byte >>= bitnum;
 
433
        node = Mem4(stringtable+8);
 
434
        while (!done) {
 
435
          nodetype = Mem1(node);
 
436
          node++;
 
437
          switch (nodetype) {
 
438
          case 0x00: /* non-leaf node */
 
439
            if (byte & 1) 
 
440
              node = Mem4(node+4);
 
441
            else
 
442
              node = Mem4(node+0);
 
443
            if (bitnum == 7) {
 
444
              bitnum = 0;
 
445
              addr++;
 
446
              byte = Mem1(addr);
 
447
            }
 
448
            else {
 
449
              bitnum++;
 
450
              byte >>= 1;
 
451
            }
 
452
            break;
 
453
          case 0x01: /* string terminator */
 
454
            done = 1;
 
455
            break;
 
456
          case 0x02: /* single character */
 
457
            ch = Mem1(node);
 
458
            switch (iosys_mode) {
 
459
            case iosys_Glk:
 
460
              glk_put_char(ch);
 
461
              break;
 
462
            case iosys_Filter: 
 
463
              ival = ch & 0xFF;
 
464
              if (!substring) {
 
465
                push_callstub(0x11, 0);
 
466
                substring = TRUE;
 
467
              }
 
468
              pc = addr;
 
469
              push_callstub(0x10, bitnum);
 
470
              enter_function(iosys_rock, 1, &ival);
 
471
              return;
 
472
            }
 
473
            node = Mem4(stringtable+8);
 
474
            break;
 
475
          case 0x04: /* single Unicode character */
 
476
            ival = Mem4(node);
 
477
            switch (iosys_mode) {
 
478
            case iosys_Glk:
 
479
              glkio_unichar_han_ptr(ival);
 
480
              break;
 
481
            case iosys_Filter: 
 
482
              if (!substring) {
 
483
                push_callstub(0x11, 0);
 
484
                substring = TRUE;
 
485
              }
 
486
              pc = addr;
 
487
              push_callstub(0x10, bitnum);
 
488
              enter_function(iosys_rock, 1, &ival);
 
489
              return;
 
490
            }
 
491
            node = Mem4(stringtable+8);
 
492
            break;
 
493
          case 0x03: /* C string */
 
494
            switch (iosys_mode) {
 
495
            case iosys_Glk:
 
496
              for (; (ch=Mem1(node)) != '\0'; node++) 
 
497
                glk_put_char(ch);
 
498
              node = Mem4(stringtable+8);
 
499
              break;
 
500
            case iosys_Filter:
 
501
              if (!substring) {
 
502
                push_callstub(0x11, 0);
 
503
                substring = TRUE;
 
504
              }
 
505
              pc = addr;
 
506
              push_callstub(0x10, bitnum);
 
507
              inmiddle = 0xE0;
 
508
              addr = node;
 
509
              done = 2;
 
510
              break;
 
511
            default:
 
512
              node = Mem4(stringtable+8);
 
513
              break;
 
514
            }
 
515
            break;
 
516
          case 0x05: /* C Unicode string */
 
517
            switch (iosys_mode) {
 
518
            case iosys_Glk:
 
519
              for (; (ival=Mem4(node)) != 0; node+=4) 
 
520
                glkio_unichar_han_ptr(ival);
 
521
              node = Mem4(stringtable+8);
 
522
              break;
 
523
            case iosys_Filter:
 
524
              if (!substring) {
 
525
                push_callstub(0x11, 0);
 
526
                substring = TRUE;
 
527
              }
 
528
              pc = addr;
 
529
              push_callstub(0x10, bitnum);
 
530
              inmiddle = 0xE2;
 
531
              addr = node;
 
532
              done = 2;
 
533
              break;
 
534
            default:
 
535
              node = Mem4(stringtable+8);
 
536
              break;
 
537
            }
 
538
            break;
 
539
          case 0x08:
 
540
          case 0x09:
 
541
          case 0x0A:
 
542
          case 0x0B: 
 
543
            {
 
544
              glui32 oaddr;
 
545
              int otype;
 
546
              oaddr = Mem4(node);
 
547
              if (nodetype == 0x09 || nodetype == 0x0B)
 
548
                oaddr = Mem4(oaddr);
 
549
              otype = Mem1(oaddr);
 
550
              if (!substring) {
 
551
                push_callstub(0x11, 0);
 
552
                substring = TRUE;
 
553
              }
 
554
              if (otype >= 0xE0 && otype <= 0xFF) {
 
555
                pc = addr;
 
556
                push_callstub(0x10, bitnum);
 
557
                inmiddle = 0;
 
558
                addr = oaddr;
 
559
                done = 2;
 
560
              }
 
561
              else if (otype >= 0xC0 && otype <= 0xDF) {
 
562
                glui32 argc;
 
563
                glui32 *argv;
 
564
                if (nodetype == 0x0A || nodetype == 0x0B) {
 
565
                  argc = Mem4(node+4);
 
566
                  argv = pop_arguments(argc, node+8);
 
567
                }
 
568
                else {
 
569
                  argc = 0;
 
570
                  argv = NULL;
 
571
                }
 
572
                pc = addr;
 
573
                push_callstub(0x10, bitnum);
 
574
                enter_function(oaddr, argc, argv);
 
575
                return;
 
576
              }
 
577
              else {
 
578
                fatal_error("Unknown object while decoding string indirect reference.");
 
579
              }
 
580
            }
 
581
            break;
 
582
          default:
 
583
            fatal_error("Unknown entity in string decoding.");
 
584
            break;
 
585
          }
 
586
        }
 
587
        if (done > 1) {
 
588
          continue; /* restart the top-level loop */
 
589
        }
 
590
      }
 
591
    }
 
592
    else if (type == 0xE0) {
 
593
      switch (iosys_mode) {
 
594
      case iosys_Glk:
 
595
        while (1) {
 
596
          ch = Mem1(addr);
 
597
          addr++;
 
598
          if (ch == '\0')
 
599
            break;
 
600
          glk_put_char(ch);
 
601
        }
 
602
        break;
 
603
      case iosys_Filter:
 
604
        if (!substring) {
 
605
          push_callstub(0x11, 0);
 
606
          substring = TRUE;
 
607
        }
 
608
        ch = Mem1(addr);
 
609
        addr++;
 
610
        if (ch != '\0') {
 
611
          ival = ch & 0xFF;
 
612
          pc = addr;
 
613
          push_callstub(0x13, 0);
 
614
          enter_function(iosys_rock, 1, &ival);
 
615
          return;
 
616
        }
 
617
        break;
 
618
      }
 
619
    }
 
620
    else if (type == 0xE2) {
 
621
      switch (iosys_mode) {
 
622
      case iosys_Glk:
 
623
        while (1) {
 
624
          ival = Mem4(addr);
 
625
          addr+=4;
 
626
          if (ival == 0)
 
627
            break;
 
628
          glkio_unichar_han_ptr(ival);
 
629
        }
 
630
        break;
 
631
      case iosys_Filter:
 
632
        if (!substring) {
 
633
          push_callstub(0x11, 0);
 
634
          substring = TRUE;
 
635
        }
 
636
        ival = Mem4(addr);
 
637
        addr+=4;
 
638
        if (ival != 0) {
 
639
          pc = addr;
 
640
          push_callstub(0x14, 0);
 
641
          enter_function(iosys_rock, 1, &ival);
 
642
          return;
 
643
        }
 
644
        break;
 
645
      }
 
646
    }
 
647
    else if (type >= 0xE0 && type <= 0xFF) {
 
648
      fatal_error("Attempt to print unknown type of string.");
 
649
    }
 
650
    else {
 
651
      fatal_error("Attempt to print non-string.");
 
652
    }
 
653
 
 
654
    if (!substring) {
 
655
      /* Just get straight out. */
 
656
      alldone = TRUE;
 
657
    }
 
658
    else {
 
659
      /* Pop a stub and see what's to be done. */
 
660
      addr = pop_callstub_string(&bitnum);
 
661
      if (addr == 0) {
 
662
        alldone = TRUE;
 
663
      }
 
664
      else {
 
665
        inmiddle = 0xE1;
 
666
      }
 
667
    }
 
668
  }
 
669
}
 
670
 
 
671
/* stream_get_table():
 
672
   Get the current table address. 
 
673
*/
 
674
glui32 stream_get_table()
 
675
{
 
676
  return stringtable;
 
677
}
 
678
 
 
679
/* stream_set_table():
 
680
   Set the current table address, and rebuild decoding cache. 
 
681
*/
 
682
void stream_set_table(glui32 addr)
 
683
{
 
684
  if (stringtable == addr)
 
685
    return;
 
686
 
 
687
  /* Drop cache. */
 
688
  if (tablecache_valid) {
 
689
    if (tablecache.type == 0)
 
690
      dropcache(tablecache.u.branches);
 
691
    tablecache.u.branches = NULL;
 
692
    tablecache_valid = FALSE;
 
693
  }
 
694
 
 
695
  stringtable = addr;
 
696
 
 
697
  if (stringtable) {
 
698
    /* Build cache. We can only do this if the table is entirely in ROM. */
 
699
    glui32 tablelen = Mem4(stringtable);
 
700
    glui32 rootaddr = Mem4(stringtable+8);
 
701
    if (stringtable+tablelen <= ramstart && !never_cache_stringtable) {
 
702
      buildcache(&tablecache, rootaddr, CACHEBITS, 0);
 
703
      /* dumpcache(&tablecache, 1, 0); */
 
704
      tablecache_valid = TRUE;
 
705
    }
 
706
  }
 
707
}
 
708
 
 
709
static void buildcache(cacheblock_t *cablist, glui32 nodeaddr, int depth,
 
710
  int mask)
 
711
{
 
712
  int ix, type;
 
713
 
 
714
  type = Mem1(nodeaddr);
 
715
 
 
716
  if (type == 0 && depth == CACHEBITS) {
 
717
    cacheblock_t *list, *cab;
 
718
    list = (cacheblock_t *)glulx_malloc(sizeof(cacheblock_t) * CACHESIZE);
 
719
    buildcache(list, nodeaddr, 0, 0);
 
720
    cab = &(cablist[mask]);
 
721
    cab->type = 0;
 
722
    cab->depth = CACHEBITS;
 
723
    cab->u.branches = list;
 
724
    return;
 
725
  }
 
726
 
 
727
  if (type == 0) {
 
728
    glui32 leftaddr  = Mem4(nodeaddr+1);
 
729
    glui32 rightaddr = Mem4(nodeaddr+5);
 
730
    buildcache(cablist, leftaddr, depth+1, mask);
 
731
    buildcache(cablist, rightaddr, depth+1, (mask | (1 << depth)));
 
732
    return;
 
733
  }
 
734
 
 
735
  /* Leaf node. */
 
736
  nodeaddr++;
 
737
  for (ix = mask; ix < CACHESIZE; ix += (1 << depth)) {
 
738
    cacheblock_t *cab = &(cablist[ix]);
 
739
    cab->type = type;
 
740
    cab->depth = depth;
 
741
    switch (type) {
 
742
    case 0x02:
 
743
      cab->u.ch = Mem1(nodeaddr);
 
744
      break;
 
745
    case 0x04:
 
746
      cab->u.uch = Mem4(nodeaddr);
 
747
      break;
 
748
    case 0x03:
 
749
    case 0x05:
 
750
    case 0x0A:
 
751
    case 0x0B:
 
752
      cab->u.addr = nodeaddr;
 
753
      break;
 
754
    case 0x08:
 
755
    case 0x09:
 
756
      cab->u.addr = Mem4(nodeaddr);
 
757
      break;
 
758
    }
 
759
  }
 
760
}
 
761
 
 
762
#if 0
 
763
#include <stdio.h>
 
764
static void dumpcache(cacheblock_t *cablist, int count, int indent)
 
765
{
 
766
  int ix, jx;
 
767
 
 
768
  for (ix=0; ix<count; ix++) {
 
769
    cacheblock_t *cab = &(cablist[ix]); 
 
770
    for (jx=0; jx<indent; jx++)
 
771
      printf("  ");
 
772
    printf("%X: ", ix);
 
773
    switch (cab->type) {
 
774
    case 0:
 
775
      printf("...\n");
 
776
      dumpcache(cab->u.branches, CACHESIZE, indent+1);
 
777
      break;
 
778
    case 1:
 
779
      printf("<EOS>\n");
 
780
      break;
 
781
    case 2:
 
782
      printf("0x%02X", cab->u.ch);
 
783
      if (cab->u.ch < 32)
 
784
        printf(" ''\n");
 
785
      else
 
786
        printf(" '%c'\n", cab->u.ch);
 
787
      break;
 
788
    default:
 
789
      printf("type %02X, address %06lX\n", cab->type, cab->u.addr);
 
790
      break;
 
791
    }
 
792
  }
 
793
}
 
794
#endif /* 0 */
 
795
 
 
796
static void dropcache(cacheblock_t *cablist)
 
797
{
 
798
  int ix;
 
799
  for (ix=0; ix<CACHESIZE; ix++) {
 
800
    cacheblock_t *cab = &(cablist[ix]);
 
801
    if (cab->type == 0) {
 
802
      dropcache(cab->u.branches);
 
803
      cab->u.branches = NULL;
 
804
    }
 
805
  }
 
806
  glulx_free(cablist);
 
807
}
 
808
 
 
809
/* This misbehaves if a Glk function has more than one S argument. */
 
810
 
 
811
#define STATIC_TEMP_BUFSIZE (127)
 
812
static char temp_buf[STATIC_TEMP_BUFSIZE+1];
 
813
 
 
814
char *make_temp_string(glui32 addr)
 
815
{
 
816
  int ix, len;
 
817
  glui32 addr2;
 
818
  char *res, *cx;
 
819
 
 
820
  if (Mem1(addr) != 0xE0)
 
821
    fatal_error("String argument to a Glk call must be unencoded.");
 
822
  addr++;
 
823
 
 
824
  for (addr2=addr; Mem1(addr2); addr2++) { };
 
825
  len = (addr2 - addr);
 
826
  if (len < STATIC_TEMP_BUFSIZE) {
 
827
    res = temp_buf;
 
828
  }
 
829
  else {
 
830
    res = (char *)glulx_malloc(len+1);
 
831
    if (!res) 
 
832
      fatal_error("Unable to allocate space for string argument to Glk call.");
 
833
  }
 
834
  
 
835
  for (ix=0, addr2=addr; ix<len; ix++, addr2++) {
 
836
    res[ix] = Mem1(addr2);
 
837
  }
 
838
  res[len] = '\0';
 
839
 
 
840
  return res;
 
841
}
 
842
 
 
843
glui32 *make_temp_ustring(glui32 addr)
 
844
{
 
845
  int ix, len;
 
846
  glui32 addr2;
 
847
  glui32 *res, *cx;
 
848
 
 
849
  if (Mem1(addr) != 0xE2)
 
850
    fatal_error("Ustring argument to a Glk call must be unencoded.");
 
851
  addr+=4;
 
852
 
 
853
  for (addr2=addr; Mem4(addr2); addr2+=4) { };
 
854
  len = (addr2 - addr) / 4;
 
855
  if ((len+1)*4 < STATIC_TEMP_BUFSIZE) {
 
856
    res = (glui32 *)temp_buf;
 
857
  }
 
858
  else {
 
859
    res = (glui32 *)glulx_malloc((len+1)*4);
 
860
    if (!res) 
 
861
      fatal_error("Unable to allocate space for ustring argument to Glk call.");
 
862
  }
 
863
  
 
864
  for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
 
865
    res[ix] = Mem4(addr2);
 
866
  }
 
867
  res[len] = 0;
 
868
 
 
869
  return res;
 
870
}
 
871
 
 
872
void free_temp_string(char *str)
 
873
{
 
874
  if (str && str != temp_buf) 
 
875
    glulx_free(str);
 
876
}
 
877
 
 
878
void free_temp_ustring(glui32 *str)
 
879
{
 
880
  if (str && str != (glui32 *)temp_buf) 
 
881
    glulx_free(str);
 
882
}
 
883