Inform - Support - Patches

About Patches  

Compiler  
Library  

DM4 Errata  

Issue C63105     [previous patch]

Support $MAX_GLOBAL_VARIABLES memory setting in Z-code
Submitted by: Jesse McGrew     Appeared in: Compiler 6.31 or before     Fixed in: -
Problem

Inform 7 games tend to use a lot of global variables, especially if multiple extensions are used, and updating I7 to use the 6/11 library will require even more. This patch allows the $MAX_GLOBAL_VARIABLES setting to be used for Z-code games (but not modules), just as it's used for Glulx games. Variables past 255 ("high globals") are stored in the Z-machine's global variable table, but they're accessed with pointer operations instead of being used directly as operands.

This conversion takes place at the assembly level: instead of assembling an opcode which uses a high global as an operand or store result, those operands are changed to stack references, and @loadw/@storew opcodes are assembled before and after the instruction as necessary. The change should be transparent to authors, even when using inline assembly language. Opcodes which use the VARIAB operand rule (@inc, @store, @pull, etc.) are rewritten entirely when used with a high global; the indirect '@store [var_number] value' form is not rewritten, and the Z-machine spec is silent on what to do when var_number is greater than 255, but the straightforward implementation used by Frotz and Nitfol does the right thing.

Solution

Apply the following patch.

diff -c inform631-old/arrays.c inform631_source/arrays.c
*** inform631-old/arrays.c	Sat Jan 28 06:12:57 2006
--- inform631_source/arrays.c	Mon Jul 17 07:37:58 2006
***************
*** 19,40 ****
  /*                                        global variable (counting 0 - 239) */
  /*                                                                           */
  /*   The "dynamic array area" is the Z-machine area holding the current      */
! /*   values of the global variables (in 240x2 = 480 bytes) followed by any   */
! /*   (dynamic) arrays which may be defined.  Owing to a poor choice of name  */
! /*   some years ago, this is also called the "static data area", which is    */
! /*   why the memory setting for its maximum extent is "MAX_STATIC_DATA".     */
! /*                                                                           */
! /*   In Glulx, that 240 is changed to MAX_GLOBAL_VAR_NUMBER, and we take     */
! /*   correspondingly more space for the globals. This *really* ought to be   */
! /*   split into two segments.                                                */
  /* ------------------------------------------------------------------------- */
  int     *dynamic_array_area;           /* See above                          */
  int32   *global_initial_value;

  int no_globals;                        /* Number of global variables used
                                            by the programmer (Inform itself
!                                           uses the top seven -- but these do
!                                           not count)                         */
                                         /* In Glulx, Inform uses the bottom
                                            ten.                               */

--- 19,37 ----
  /*                                        global variable (counting 0 - 239) */
  /*                                                                           */
  /*   The "dynamic array area" is the Z-machine area holding the current      */
! /*   values of the global variables (WORDSIZE * MAX_GLOBAL_VARIABLES bytes)  */
! /*   followed by any (dynamic) arrays which may be defined.  Owing to a poor */
! /*   choice of name some years ago, this is also called the "static data     */
! /*   area", which is why the memory setting for its maximum extent is        */
! /*   "MAX_STATIC_DATA".                                                      */
  /* ------------------------------------------------------------------------- */
  int     *dynamic_array_area;           /* See above                          */
  int32   *global_initial_value;

  int no_globals;                        /* Number of global variables used
                                            by the programmer (Inform itself
!                                           uses 249-255 -- but these are only
!                                           counted once they're reached)      */
                                         /* In Glulx, Inform uses the bottom
                                            ten.                               */

***************
*** 240,253 ****
          array_symbols[no_arrays] = i;
      }
      else
!     {   if (!glulx_mode && no_globals==233)
!         {   error("All 233 global variables already declared");
!             panic_mode_error_recovery();
!             return;
!         }
!         if (glulx_mode && no_globals==MAX_GLOBAL_VARIABLES)
          {   error_numbered("All global variables already declared; max is",
!                 MAX_GLOBAL_VARIABLES);
              panic_mode_error_recovery();
              return;
          }
--- 237,248 ----
          array_symbols[no_arrays] = i;
      }
      else
!     {   /* skip over the reserved globals for Z-code */
!         if (!glulx_mode && no_globals == 255 - MAX_LOCAL_VARIABLES - 6)
!             no_globals = 255 - MAX_LOCAL_VARIABLES + 1;
!         if (no_globals==MAX_GLOBAL_VARIABLES)
          {   error_numbered("All global variables already declared; max is",
!                 glulx_mode ? MAX_GLOBAL_VARIABLES : MAX_GLOBAL_VARIABLES - 7);
              panic_mode_error_recovery();
              return;
          }
diff -c inform631-old/asm.c inform631_source/asm.c
*** inform631-old/asm.c	Sat Jan 28 06:13:46 2006
--- inform631_source/asm.c	Mon Jul 17 09:37:50 2006
***************
*** 697,702 ****
--- 697,748 ----
      }
  }

+ static void assemblez_rewrite_for_higlobals(assembly_instruction *AI)
+ {
+     int32 num = AI->internal_number;
+     int32 dest = AI->store_variable_number;
+     assembly_operand table_op, var_op;
+
+     ASSERT_ZCODE();
+
+     table_op.type = LONG_CONSTANT_OT;
+     table_op.value = ipv__end_SC;
+     table_op.marker = INCON_MV;
+
+     var_op.value = AI->operand[0].value - MAX_LOCAL_VARIABLES;
+     var_op.marker = 0;
+     set_constant_ot(&var_op);
+
+     switch (num) {
+         case dec_chk_zc:
+         case inc_chk_zc:
+         case inc_zc:
+         case dec_zc:
+             assemblez_2_to(loadw_zc, table_op, var_op, stack_pointer);
+             stack_pointer.type = SHORT_CONSTANT_OT;
+             assemblez_1(num, stack_pointer);
+             stack_pointer.type = VARIABLE_OT;
+             assemblez_3(storew_zc, table_op, var_op, stack_pointer);
+             break;
+         case store_zc:
+             assemblez_3(storew_zc, table_op, var_op, AI->operand[1]);
+             break;
+         case pull_zc:
+             assemblez_3(storew_zc, table_op, var_op, stack_pointer);
+             break;
+         case load_zc:
+             assemblez_2_to(loadw_zc, table_op, var_op, stack_pointer);
+             var_op.value = dest;
+             var_op.marker = 0;
+             set_constant_ot(&var_op);
+             assemblez_1(pull_zc, var_op);
+             break;
+         default:
+             compiler_error("Shouldn't be trying to rewrite this opcode");
+             break;
+     }
+ }
+
  extern void assemblez_instruction(assembly_instruction *AI)
  {
      uchar *start_pc, *operands_pc;
***************
*** 704,709 ****
--- 750,757 ----
      int operand_rules, min, max, no_operands_given, at_seq_point = FALSE;
      assembly_operand o1, o2;
      opcodez opco;
+     assembly_instruction higlobAI;
+     int need_higlobAI = 0;

      ASSERT_ZCODE();

***************
*** 742,747 ****
--- 790,832 ----
      if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
          opco.no = VAR;

+     /* 0. Check for opcodes that need to be rewritten for high globals.
+        For other opcodes, convert high global variable operands to use the stack. */
+
+     if ((operand_rules == VARIAB) && (no_operands_given >= 1) &&
+         (is_constant_ot(AI->operand[0].type)) && (AI->operand[0].value > 255)) {
+         if (at_seq_point) sequence_point_follows = TRUE;
+         assemblez_rewrite_for_higlobals(AI);
+         return;
+     }
+
+     for (j = no_operands_given - 1; j >= 0; j--) {
+         assembly_operand op = AI->operand[j];
+         if (op.type == VARIABLE_OT && op.value > 255) {
+             o1.type = LONG_CONSTANT_OT;
+             o1.value = ipv__end_SC;
+             o1.marker = INCON_MV;
+             o2.value = op.value - MAX_LOCAL_VARIABLES;
+             o2.marker = 0;
+             set_constant_ot(&o2);
+
+             higlobAI.internal_number = loadw_zc;
+             higlobAI.operand_count = 2;
+             higlobAI.operand[0] = o1;
+             higlobAI.operand[1] = o2;
+             higlobAI.store_variable_number = stack_pointer.value;
+             higlobAI.branch_label_number = -1;
+
+             if (at_seq_point) {
+                 sequence_point_follows = TRUE;
+                 at_seq_point = FALSE;
+             }
+             assemblez_instruction(&higlobAI);
+
+             AI->operand[j] = stack_pointer;
+         }
+     }
+
      /* 1. Write the opcode byte(s) */

      start_pc = zcode_holding_area + zcode_ha_size;
***************
*** 837,854 ****
      /* 4. Assemble a Store destination, if needed */

      if ((AI->store_variable_number) != -1)
!     {   o1.type = VARIABLE_OT;
!         o1.value = AI->store_variable_number;
!         variable_usage[o1.value] = TRUE;
!         o1.marker = 0;
!
!         /*  Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
!             are used as scratch workspace, so need no mapping between
!             modules and story files: nor do local variables 0 to 15  */
!
!         if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
!             o1.marker = VARIABLE_MV;
!         write_operand(o1);
      }

      /* 5. Assemble a branch, if needed */
--- 922,970 ----
      /* 4. Assemble a Store destination, if needed */

      if ((AI->store_variable_number) != -1)
!     {
!         if (AI->store_variable_number > 255) {
!             /* For high global variables, we store to the stack, then copy
!                the value to memory. */
!             write_operand(stack_pointer);
!
!             if (AI->branch_label_number != -1) {
!                 compiler_error("I can't combine branching with storing to a high global variable");
!                 return;
!             }
!
!             o1.type = LONG_CONSTANT_OT;
!             o1.value = ipv__end_SC;
!             o1.marker = INCON_MV;
!             o2.value = AI->store_variable_number - MAX_LOCAL_VARIABLES;
!             o2.marker = 0;
!             set_constant_ot(&o2);
!
!             higlobAI.internal_number = storew_zc;
!             higlobAI.operand_count = 3;
!             higlobAI.operand[0] = o1;
!             higlobAI.operand[1] = o2;
!             higlobAI.operand[2] = stack_pointer;
!             higlobAI.store_variable_number = -1;
!             higlobAI.branch_label_number = -1;
!
!             AI->store_variable_number = 0;
!             need_higlobAI = 1;
!         }
!         else {
!             o1.type = VARIABLE_OT;
!             o1.value = AI->store_variable_number;
!             variable_usage[o1.value] = TRUE;
!             o1.marker = 0;
!
!             /*  Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
!                 are used as scratch workspace, so need no mapping between
!                 modules and story files: nor do local variables 0 to 15  */
!
!             if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
!                 o1.marker = VARIABLE_MV;
!             write_operand(o1);
!         }
      }

      /* 5. Assemble a branch, if needed */
***************
*** 934,939 ****
--- 1050,1059 ----
          printf("\n");
      }

+     /* 6. Assemble the left over storew instruction for high globals, if any. */
+
+     if (need_higlobAI) assemblez_instruction(&higlobAI);
+
      if (module_switch) flush_link_data();

      return;
diff -c inform631-old/inform.c inform631_source/inform.c
*** inform631-old/inform.c	Fri Feb 10 02:18:42 2006
--- inform631_source/inform.c	Sun Jul 23 08:50:52 2006
***************
*** 91,100 ****
        MAX_LOCAL_VARIABLES = 16;
        warning("You cannot change MAX_LOCAL_VARIABLES in Z-code; resetting to 16");
      }
-     if (MAX_GLOBAL_VARIABLES != 240) {
-       MAX_GLOBAL_VARIABLES = 240;
-       warning("You cannot change MAX_GLOBAL_VARIABLES in Z-code; resetting to 240");
-     }
    }
    else {
      /* Glulx */
--- 91,96 ----
***************
*** 117,122 ****
--- 113,124 ----
      /* This is because the keyword table in the lexer only has 120
         entries. */
    }
+   if (module_switch && MAX_GLOBAL_VARIABLES > 240) {
+     MAX_GLOBAL_VARIABLES = 240;
+     warning("MAX_GLOBAL_VARIABLES cannot exceed 240 in module mode; resetting to 240");
+     /* Raising this limit involves changes to linker.c. */
+   }
+
    if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
      DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
      warning_numbered(
diff -c inform631-old/linker.c inform631_source/linker.c
*** inform631-old/linker.c	Sat Jan 28 06:14:30 2006
--- inform631_source/linker.c	Sun Jul 23 10:15:10 2006
***************
*** 499,504 ****
--- 499,509 ----
      int32 last, i, j, k, l, m, vn, len, size, link_offset, module_size, map,
            max_property_identifier, symbols_base = no_symbols;

+     if (MAX_GLOBAL_VARIABLES > 240)
+     {   MAX_GLOBAL_VARIABLES = 240;
+         warning("MAX_GLOBAL_VARIABLES cannot exceed 240 when using modules; resetting to 240");
+     };
+
      strcpy(current_module_filename, given_filename);

      /* (1) Load in the module to link */
diff -c inform631-old/memory.c inform631_source/memory.c
*** inform631-old/memory.c	Sat Jan 28 06:14:30 2006
--- inform631_source/memory.c	Mon Jul 17 05:22:23 2006
***************
*** 283,289 ****
          MAX_INDIV_PROP_TABLE_SIZE = 15000;
          MAX_ARRAYS = 128;

!         MAX_GLOBAL_VARIABLES_z = 240;
          MAX_GLOBAL_VARIABLES_g = 512;
      }
      if (size_flag == LARGE_SIZE)
--- 283,289 ----
          MAX_INDIV_PROP_TABLE_SIZE = 15000;
          MAX_ARRAYS = 128;

!         MAX_GLOBAL_VARIABLES_z = 512;
          MAX_GLOBAL_VARIABLES_g = 512;
      }
      if (size_flag == LARGE_SIZE)
***************
*** 635,641 ****
      if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
      {   printf(
  "  MAX_GLOBAL_VARIABLES is the number of global variables allowed in the \n\
!   program. (Glulx only)\n");
          return;
      }
      if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
--- 635,641 ----
      if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
      {   printf(
  "  MAX_GLOBAL_VARIABLES is the number of global variables allowed in the \n\
!   program.\n");
          return;
      }
      if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
diff -c inform631-old/tables.c inform631_source/tables.c
*** inform631-old/tables.c	Sat Jan 28 06:14:56 2006
--- inform631_source/tables.c	Sun Jul 23 08:29:23 2006
***************
*** 454,460 ****
      for (i=0; i<dynamic_array_area_size; i++)
          p[mark++] = dynamic_array_area[i];

!     for (i=0; i<240; i++)
      {   j=global_initial_value[i];
          p[globals_at+i*2]   = j/256; p[globals_at+i*2+1] = j%256;
      }
--- 454,460 ----
      for (i=0; i<dynamic_array_area_size; i++)
          p[mark++] = dynamic_array_area[i];

!     for (i=0; i<MAX_GLOBAL_VARIABLES; i++)
      {   j=global_initial_value[i];
          p[globals_at+i*2]   = j/256; p[globals_at+i*2+1] = j%256;
      }
***************
*** 907,916 ****

              printf("\
  %6d classes (maximum %2d)         %6d objects (maximum %3d)\n\
! %6d global vars (maximum 233)    %6d variable/array space (maximum %d)\n",
                   no_classes, MAX_CLASSES,
                   no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)),
!                  no_globals,
                   dynamic_array_area_size, MAX_STATIC_DATA);

              printf(
--- 907,916 ----

              printf("\
  %6d classes (maximum %2d)         %6d objects (maximum %3d)\n\
! %6d global vars (maximum %3d)    %6d variable/array space (maximum %d)\n",
                   no_classes, MAX_CLASSES,
                   no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)),
!                  no_globals, MAX_GLOBAL_VARIABLES - 7,
                   dynamic_array_area_size, MAX_STATIC_DATA);

              printf(
***************
*** 1571,1580 ****

              printf("\
  %6d classes (maximum %2d)         %6d objects (maximum %3d)\n\
! %6d global vars (maximum 233)    %6d variable/array space (maximum %d)\n",
                   no_classes, MAX_CLASSES,
                   no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)),
!                  no_globals,
                   dynamic_array_area_size, MAX_STATIC_DATA);

              printf(
--- 1571,1580 ----

              printf("\
  %6d classes (maximum %2d)         %6d objects (maximum %3d)\n\
! %6d global vars (maximum %3d)    %6d variable/array space (maximum %d)\n",
                   no_classes, MAX_CLASSES,
                   no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)),
!                  no_globals, MAX_GLOBAL_VARIABLES,
                   dynamic_array_area_size, MAX_STATIC_DATA);

              printf(


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.