Inform - Support - Patches

About Patches  

Compiler  
Library  

DM4 Errata  

Issue C63108     [previous patch]

Glulx support for compilation control
Submitted by: Andrew Plotkin     Appeared in: Compiler 6.31 or before     Fixed in: -
Problem

The -v version switch, currently used for Z-code (for example, "-v5" or "-v8"), now additionally accepts Glulx version numbers (for example, "inform -G -v3.1.0", or "!% -Gv3.1.0" as a header comment); in either case, the -v option must appear after -G is set. This enables you to set a version number higher than the compiler's decision, which is based on the VM features used by the game.

The memory settings list now includes Glulx-specific settings only when you also include the -G option (for example, "inform -G $list"). $list is documented here.

A new Glulx-only memory setting $MAX_STACK_SIZE sets the runtime stack limit for the interpreter. (This is not quite in line with the other compiler memory options, because it doesn't affect the compiler's use of memory at all, but this is the intuitive place to put it.)

CAUTION: $MAX_STACK_SIZE defaults to 4096, a value dating back to the Inform 6 era, which corresponds to the old Z-Machine Standard recommendation of 90 nested function calls. This replaces patch C63103, which sets the max stack size to 65536. (The memory setting option is better because the limit of 4096 is fine for I6 games, and it seems a bad idea to change the footprint of all Glulx games because of an I7 requirement.) Once the current patch is incorporated into the I7 system, I7 will have to generate a "!% $MAX_STACK_SIZE=65536" header comment to achieve the same effect.

Solution

Apply the following patch.

diff -c inform63-opcodeg/asm.c inform/asm.c
*** inform63-opcodeg/asm.c	2006-10-26 16:08:32.000000000 -0400
--- inform/asm.c	2006-11-04 19:44:05.000000000 -0500
***************
*** 287,292 ****
--- 287,294 ----

      /* Codes for any unusual operand assembly rules */

+     /* Z-code: */
+
  #define VARIAB   1    /* First operand expected to be a variable name and
                           assembled to a short constant: the variable number */
  #define TEXT     2    /* One text operand, to be Z-encoded into the program */
***************
*** 295,300 ****
--- 297,306 ----
                           as long constant (the routine's packed address):
                           as if the name were prefixed by #r$ */

+     /* Glulx: (bit flags for Glulx VM features) */
+
+ #define GOP_Unicode    1   /* uses_unicode_features */
+
      /* Codes for the number of operands */

  #define TWO      1    /* 2 (with certain types of operand, compiled as VAR) */
***************
*** 555,561 ****
    { (uchar *) "callfi",     0x0161, St, 0, 3 },
    { (uchar *) "callfii",    0x0162, St, 0, 4 },
    { (uchar *) "callfiii",   0x0163, St, 0, 5 },
!   { (uchar *) "streamunichar", 0x73,  0, 0, 1 },
  };

  static opcodeg custom_opcode_g;
--- 561,567 ----
    { (uchar *) "callfi",     0x0161, St, 0, 3 },
    { (uchar *) "callfii",    0x0162, St, 0, 4 },
    { (uchar *) "callfiii",   0x0163, St, 0, 5 },
!   { (uchar *) "streamunichar", 0x73,  0, GOP_Unicode, 1 },
  };

  static opcodeg custom_opcode_g;
***************
*** 980,986 ****

      execution_never_reaches_here = ((opco.flags & Rf) != 0);

!     if (AI->internal_number == streamunichar_gc) {
          uses_unicode_features = TRUE;
      }

--- 986,992 ----

      execution_never_reaches_here = ((opco.flags & Rf) != 0);

!     if (opco.op_rules & GOP_Unicode) {
          uses_unicode_features = TRUE;
      }

diff -c inform63-opcodeg/directs.c inform/directs.c
*** inform63-opcodeg/directs.c	2006-10-26 14:55:45.000000000 -0400
--- inform/directs.c	2006-10-27 17:59:21.000000000 -0400
***************
*** 806,811 ****
--- 806,818 ----
              AO = parse_expression(CONSTANT_CONTEXT);
              if (module_switch && (AO.marker != 0))
                  error("A definite value must be given as version number");
+             else
+             if (glulx_mode)
+             {
+               warning("The Version directive does not work in Glulx. Use \
+ -vX.Y.Z instead, as either a command-line argument or a header comment.");
+               break;
+             }
              else
              {   i = AO.value;
                  if ((i<3) || (i>8))

diff -c inform63-opcodeg/files.c inform/files.c
*** inform63-opcodeg/files.c	2006-10-26 14:56:44.000000000 -0400
--- inform/files.c	2006-11-04 19:43:26.000000000 -0500
***************
*** 477,483 ****
  static void output_file_g(void)
  {   FILE *fin; char new_name[PATHLEN];
      int32 size, i, j;
!     int32 val;

      ASSERT_GLULX();

--- 477,483 ----
  static void output_file_g(void)
  {   FILE *fin; char new_name[PATHLEN];
      int32 size, i, j;
!     int32 VersionNum;

      ASSERT_GLULX();

***************
*** 499,504 ****
--- 499,526 ----
      checksum_long = 0;
      checksum_count = 0;

+     /* Determine the version number. */
+
+     VersionNum = 0x00020000;
+
+     /* Increase for various features the game may have used. */
+     if (no_unicode_chars != 0 || (uses_unicode_features)) {
+       VersionNum = 0x00030000;
+     }
+
+     /* And check if the user has requested a specific version. */
+     if (requested_glulx_version) {
+       if (requested_glulx_version < VersionNum) {
+         static char error_message_buff[256];
+         sprintf(error_message_buff, "Version 0x%08lx requested, but \
+ game features require version 0x%08lx", requested_glulx_version, VersionNum);
+         warning(error_message_buff);
+       }
+       else {
+         VersionNum = requested_glulx_version;
+       }
+     }
+
      /*  (1)  Output the header. We use sf_put here, instead of fputc,
          because the header is included in the checksum. */

***************
*** 508,521 ****
      sf_put('u');
      sf_put('l');
      /* Version number. */
!     if (no_unicode_chars == 0 && (!uses_unicode_features))
!       val = 0x00020000;
!     else
!       val = 0x00030000;
!     sf_put((val >> 24));
!     sf_put((val >> 16));
!     sf_put((val >> 8));
!     sf_put((val));
      /* RAMSTART */
      sf_put((Write_RAM_At >> 24));
      sf_put((Write_RAM_At >> 16));
--- 530,539 ----
      sf_put('u');
      sf_put('l');
      /* Version number. */
!     sf_put((VersionNum >> 24));
!     sf_put((VersionNum >> 16));
!     sf_put((VersionNum >> 8));
!     sf_put((VersionNum));
      /* RAMSTART */
      sf_put((Write_RAM_At >> 24));
      sf_put((Write_RAM_At >> 16));
***************
*** 531,543 ****
      sf_put((Out_Size >> 16));
      sf_put((Out_Size >> 8));
      sf_put((Out_Size));
!     /* STACKSIZE, which we guess at 4096. That's about enough for 90
!        nested function calls with 8 locals each -- the same capacity
!        as the Z-Spec's suggestion for Z-machine stack size. */
!     sf_put(0x00);
!     sf_put(0x00);
!     sf_put(0x10);
!     sf_put(0x00);
      /* Initial function to call. Inform sets things up so that this
         is the start of the executable-code area. */
      sf_put((Write_Code_At >> 24));
--- 549,559 ----
      sf_put((Out_Size >> 16));
      sf_put((Out_Size >> 8));
      sf_put((Out_Size));
!     /* STACKSIZE */
!     sf_put((MAX_STACK_SIZE >> 24));
!     sf_put((MAX_STACK_SIZE >> 16));
!     sf_put((MAX_STACK_SIZE >> 8));
!     sf_put((MAX_STACK_SIZE));
      /* Initial function to call. Inform sets things up so that this
         is the start of the executable-code area. */
      sf_put((Write_Code_At >> 24));

diff -c inform63-opcodeg/header.h inform/header.h
*** inform63-opcodeg/header.h	2006-10-26 14:55:45.000000000 -0400
--- inform/header.h	2006-11-04 19:43:41.000000000 -0500
***************
*** 2285,2290 ****
--- 2285,2291 ----
  extern int oddeven_packing_switch;

  extern int glulx_mode, compression_switch;
+ extern int32 requested_glulx_version;

  extern int error_format,    store_the_text,       asm_trace_setting,
      double_space_setting,   trace_fns_setting,    character_set_setting,
***************
*** 2383,2389 ****

  extern int32 MAX_STATIC_STRINGS, MAX_ZCODE_SIZE, MAX_LINK_DATA_SIZE,
             MAX_TRANSCRIPT_SIZE,  MAX_INDIV_PROP_TABLE_SIZE,
!            MAX_NUM_STATIC_STRINGS, MAX_UNICODE_CHARS;

  extern int32 MAX_OBJ_PROP_COUNT, MAX_OBJ_PROP_TABLE_SIZE;
  extern int MAX_LOCAL_VARIABLES, MAX_GLOBAL_VARIABLES;
--- 2384,2391 ----

  extern int32 MAX_STATIC_STRINGS, MAX_ZCODE_SIZE, MAX_LINK_DATA_SIZE,
             MAX_TRANSCRIPT_SIZE,  MAX_INDIV_PROP_TABLE_SIZE,
!            MAX_NUM_STATIC_STRINGS, MAX_UNICODE_CHARS,
!            MAX_STACK_SIZE;

  extern int32 MAX_OBJ_PROP_COUNT, MAX_OBJ_PROP_TABLE_SIZE;
  extern int MAX_LOCAL_VARIABLES, MAX_GLOBAL_VARIABLES;

diff -c inform63-opcodeg/inform.c inform/inform.c
*** inform63-opcodeg/inform.c	2006-10-26 14:55:45.000000000 -0400
--- inform/inform.c	2006-10-27 18:04:44.000000000 -0400
***************
*** 24,33 ****
  /*   Version control                                                         */
  /* ------------------------------------------------------------------------- */

! /* This stuff is Z-code only, for now. It might handle multiple Glulx
!    versions someday, if needed. */
!
! int version_number,      /* 3 to 8                                           */
      instruction_set_number,
                           /* 3 to 6: versions 7 and 8 use instruction set of
                              version 5                                        */
--- 24,30 ----
  /*   Version control                                                         */
  /* ------------------------------------------------------------------------- */

! int version_number,      /* 3 to 8 (Z-code)                                  */
      instruction_set_number,
                           /* 3 to 6: versions 7 and 8 use instruction set of
                              version 5                                        */
***************
*** 35,40 ****
--- 32,39 ----
  int32 scale_factor,      /* packed address multiplier                        */
      length_scale_factor; /* length-in-header multiplier                      */

+ int32 requested_glulx_version;
+
  extern void select_version(int vn)
  {   version_number = vn;
      extend_memory_map = FALSE;
***************
*** 51,56 ****
--- 50,80 ----
      if ((version_number==7)||(version_number==8)) instruction_set_number = 5;
  }

+ static int select_glulx_version(char *str)
+ {
+   /* Parse an "X.Y.Z" style version number, and store it for later use. */
+   char *cx = str;
+   int major=0, minor=0, patch=0;
+
+   while (isdigit(*cx))
+     major = major*10 + ((*cx++)-'0');
+   if (*cx == '.') {
+     cx++;
+     while (isdigit(*cx))
+       minor = minor*10 + ((*cx++)-'0');
+     if (*cx == '.') {
+       cx++;
+       while (isdigit(*cx))
+         patch = patch*10 + ((*cx++)-'0');
+     }
+   }
+
+   requested_glulx_version = ((major & 0x7FFF) << 16)
+     + ((minor & 0xFF) << 8)
+     + (patch & 0xFF);
+   return (cx - str);
+ }
+
  /* ------------------------------------------------------------------------- */
  /*   Target: variables which vary between the Z-machine and Glulx            */
  /* ------------------------------------------------------------------------- */
***************
*** 250,255 ****
--- 274,280 ----

      compression_switch = TRUE;
      glulx_mode = FALSE;
+     requested_glulx_version = 0;
  }

  /* ------------------------------------------------------------------------- */
***************
*** 1237,1243 ****
          case 's': statistics_switch = state; break;
          case 't': asm_trace_setting=2; break;
          case 'u': optimise_switch = state; break;
!         case 'v': if ((cmode==0) && (version_set_switch)) { s=2; break; }
                    version_set_switch = TRUE; s=2;
                    switch(p[i+1])
                    {   case '3': select_version(3); break;
--- 1262,1269 ----
          case 's': statistics_switch = state; break;
          case 't': asm_trace_setting=2; break;
          case 'u': optimise_switch = state; break;
!         case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
!                   if ((cmode==0) && (version_set_switch)) { s=2; break; }
                    version_set_switch = TRUE; s=2;
                    switch(p[i+1])
                    {   case '3': select_version(3); break;

diff -c inform63-opcodeg/memory.c inform/memory.c
*** inform63-opcodeg/memory.c	2006-10-26 14:55:45.000000000 -0400
--- inform/memory.c	2006-10-26 16:53:56.000000000 -0400
***************
*** 175,180 ****
--- 175,181 ----
  int NUM_ATTR_BYTES;
  int32 MAX_NUM_STATIC_STRINGS;
  int32 MAX_UNICODE_CHARS;
+ int32 MAX_STACK_SIZE;

  /* The way memory sizes are set causes great nuisance for those parameters
     which have different defaults under Z-code and Glulx. We have to get
***************
*** 212,236 ****
      printf("|  %25s = %-7d |\n","MAX_LABELS",MAX_LABELS);
      printf("|  %25s = %-7d |\n","MAX_LINESPACE",MAX_LINESPACE);
      printf("|  %25s = %-7d |\n","MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE);
!     printf("|  %25s = %-7d |\n","MAX_LOCAL_VARIABLES",MAX_LOCAL_VARIABLES);
      printf("|  %25s = %-7d |\n","MAX_LOW_STRINGS",MAX_LOW_STRINGS);
!     printf("|  %25s = %-7d |\n","MAX_NUM_STATIC_STRINGS",MAX_NUM_STATIC_STRINGS);
      printf("|  %25s = %-7d |\n","MAX_OBJECTS",MAX_OBJECTS);
!     printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_COUNT",
          MAX_OBJ_PROP_COUNT);
!     printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_TABLE_SIZE",
          MAX_OBJ_PROP_TABLE_SIZE);
      printf("|  %25s = %-7d |\n","MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
      printf("|  %25s = %-7d |\n","MAX_QTEXT_SIZE",MAX_QTEXT_SIZE);
      printf("|  %25s = %-7d |\n","MAX_SOURCE_FILES",MAX_SOURCE_FILES);
!     printf("|  %25s = %-7d |\n","MAX_SYMBOLS",MAX_SYMBOLS);
      printf("|  %25s = %-7d |\n","MAX_STATIC_DATA",MAX_STATIC_DATA);
      printf("|  %25s = %-7ld |\n","MAX_STATIC_STRINGS",
             (long int) MAX_STATIC_STRINGS);
      printf("|  %25s = %-7d |\n","SYMBOLS_CHUNK_SIZE",SYMBOLS_CHUNK_SIZE);
      printf("|  %25s = %-7ld |\n","MAX_TRANSCRIPT_SIZE",
             (long int) MAX_TRANSCRIPT_SIZE);
!     printf("|  %25s = %-7ld |\n","MAX_UNICODE_CHARS",
             (long int) MAX_UNICODE_CHARS);
      printf("|  %25s = %-7d |\n","MAX_VERBS",MAX_VERBS);
      printf("|  %25s = %-7d |\n","MAX_VERBSPACE",MAX_VERBSPACE);
--- 213,246 ----
      printf("|  %25s = %-7d |\n","MAX_LABELS",MAX_LABELS);
      printf("|  %25s = %-7d |\n","MAX_LINESPACE",MAX_LINESPACE);
      printf("|  %25s = %-7d |\n","MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE);
!     if (glulx_mode)
!       printf("|  %25s = %-7d |\n","MAX_LOCAL_VARIABLES",MAX_LOCAL_VARIABLES);
      printf("|  %25s = %-7d |\n","MAX_LOW_STRINGS",MAX_LOW_STRINGS);
!     if (glulx_mode)
!       printf("|  %25s = %-7d |\n","MAX_NUM_STATIC_STRINGS",
!         MAX_NUM_STATIC_STRINGS);
      printf("|  %25s = %-7d |\n","MAX_OBJECTS",MAX_OBJECTS);
!     if (glulx_mode)
!       printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_COUNT",
          MAX_OBJ_PROP_COUNT);
!     if (glulx_mode)
!       printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_TABLE_SIZE",
          MAX_OBJ_PROP_TABLE_SIZE);
      printf("|  %25s = %-7d |\n","MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
      printf("|  %25s = %-7d |\n","MAX_QTEXT_SIZE",MAX_QTEXT_SIZE);
      printf("|  %25s = %-7d |\n","MAX_SOURCE_FILES",MAX_SOURCE_FILES);
!     if (glulx_mode)
!       printf("|  %25s = %-7ld |\n","MAX_STACK_SIZE",
!            (long int) MAX_STACK_SIZE);
      printf("|  %25s = %-7d |\n","MAX_STATIC_DATA",MAX_STATIC_DATA);
      printf("|  %25s = %-7ld |\n","MAX_STATIC_STRINGS",
             (long int) MAX_STATIC_STRINGS);
+     printf("|  %25s = %-7d |\n","MAX_SYMBOLS",MAX_SYMBOLS);
      printf("|  %25s = %-7d |\n","SYMBOLS_CHUNK_SIZE",SYMBOLS_CHUNK_SIZE);
      printf("|  %25s = %-7ld |\n","MAX_TRANSCRIPT_SIZE",
             (long int) MAX_TRANSCRIPT_SIZE);
!     if (glulx_mode)
!       printf("|  %25s = %-7ld |\n","MAX_UNICODE_CHARS",
             (long int) MAX_UNICODE_CHARS);
      printf("|  %25s = %-7d |\n","MAX_VERBS",MAX_VERBS);
      printf("|  %25s = %-7d |\n","MAX_VERBSPACE",MAX_VERBSPACE);
***************
*** 396,401 ****
--- 406,417 ----
      NUM_ATTR_BYTES_z = 6;
      NUM_ATTR_BYTES_g = 7;
      MAX_UNICODE_CHARS = 64;
+     /* We estimate the default Glulx stack size at 4096. That's about
+        enough for 90 nested function calls with 8 locals each -- the
+        same capacity as the Z-Spec's suggestion for Z-machine stack
+        size. Note that Inform 7 wants more stack, so if you're
+        compiling an I7 game, crank this up. */
+     MAX_STACK_SIZE = 4096;

      adjust_memory_sizes();
  }
***************
*** 656,661 ****
--- 672,684 ----
    (Glulx only)\n");
          return;
      }
+     if (strcmp(command,"MAX_STACK_SIZE")==0)
+     {
+         printf(
+ "  MAX_STACK_SIZE is the maximum size (in bytes) of the interpreter stack \n\
+   during gameplay. (Glulx only)\n");
+         return;
+     }

      printf("No such memory setting as \"%s\"\n",command);

***************
*** 791,796 ****
--- 814,825 ----
              }
              if (strcmp(command,"MAX_UNICODE_CHARS")==0)
                  MAX_UNICODE_CHARS=j, flag=1;
+             if (strcmp(command,"MAX_STACK_SIZE")==0)
+             {
+                 MAX_STACK_SIZE=j, flag=1;
+                 /* Adjust up to a 256-byte boundary. */
+                 MAX_STACK_SIZE = (MAX_STACK_SIZE + 0xFF) & (~0xFF);
+             }

              if (flag==0)
                  printf("No such memory setting as \"%s\"\n", command);


Last updated 17 April 2013. This web site has not been fully supported since April 2008. Information may be out of date. This page was originally managed by Roger Firth.