1 /* ------------------------------------------------------------------------- */
2 /* "memory" : Memory management and ICL memory setting commands */
3 /* (For "memoryerror", see "errors.c") */
5 /* Part of Inform 6.35 */
6 /* copyright (c) Graham Nelson 1993 - 2021 */
8 /* Inform is free software: you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation, either version 3 of the License, or */
11 /* (at your option) any later version. */
13 /* Inform is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ *
21 /* ------------------------------------------------------------------------- */
25 int32 malloced_bytes=0; /* Total amount of memory allocated */
29 extern void *my_malloc(int32 size, char *whatfor)
32 printf("Allocating %ld bytes for %s\n",size,whatfor);
33 if (size==0) return(NULL);
34 c=(char _huge *)halloc(size,1); malloced_bytes+=size;
35 if (c==0) memory_out_error(size, 1, whatfor);
39 extern void my_realloc(void *pointer, int32 oldsize, int32 size,
43 my_free(pointer, whatfor);
46 c=halloc(size,1); malloced_bytes+=size;
47 if (c==0) memory_out_error(size, 1, whatfor);
49 printf("Increasing allocation to %ld bytes for %s was (%08lx) \
51 (long int) size,whatfor,(long int) (*(int **)pointer),
53 memcpy(c, *(int **)pointer, MIN(oldsize, size));
54 hfree(*(int **)pointer);
58 extern void *my_calloc(int32 size, int32 howmany, char *whatfor)
61 printf("Allocating %d bytes: array (%ld entries size %ld) for %s\n",
62 size*howmany,howmany,size,whatfor);
63 if ((size*howmany) == 0) return(NULL);
64 c=(void _huge *)halloc(howmany*size,1); malloced_bytes+=size*howmany;
65 if (c==0) memory_out_error(size, howmany, whatfor);
69 extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany,
70 int32 howmany, char *whatfor)
72 if (size*howmany==0) {
73 my_free(pointer, whatfor);
76 c=(void _huge *)halloc(size*howmany,1); malloced_bytes+=size*howmany;
77 if (c==0) memory_out_error(size, howmany, whatfor);
79 printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \
80 for %s was (%08lx) now (%08lx)\n",
81 ((long int)size) * ((long int)howmany),
82 (long int)howmany,(long int)size,whatfor,
83 (long int) *(int **)pointer, (long int) c);
84 memcpy(c, *(int **)pointer, MIN(size*oldhowmany, size*howmany));
85 hfree(*(int **)pointer);
91 extern void *my_malloc(int32 size, char *whatfor)
93 if (size==0) return(NULL);
94 c=malloc((size_t) size); malloced_bytes+=size;
95 if (c==0) memory_out_error(size, 1, whatfor);
97 printf("Allocating %ld bytes for %s at (%08lx)\n",
98 (long int) size,whatfor,(long int) c);
102 extern void my_realloc(void *pointer, int32 oldsize, int32 size,
106 my_free(pointer, whatfor);
109 c=realloc(*(int **)pointer, (size_t) size); malloced_bytes+=size;
110 if (c==0) memory_out_error(size, 1, whatfor);
112 printf("Increasing allocation to %ld bytes for %s was (%08lx) \
114 (long int) size,whatfor,(long int) (*(int **)pointer),
116 *(int **)pointer = c;
119 extern void *my_calloc(int32 size, int32 howmany, char *whatfor)
121 if (size*howmany==0) return(NULL);
122 c=calloc(howmany,(size_t) size); malloced_bytes+=size*howmany;
123 if (c==0) memory_out_error(size, howmany, whatfor);
125 printf("Allocating %ld bytes: array (%ld entries size %ld) \
126 for %s at (%08lx)\n",
127 ((long int)size) * ((long int)howmany),
128 (long int)howmany,(long int)size,whatfor,
133 extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany,
134 int32 howmany, char *whatfor)
136 if (size*howmany==0) {
137 my_free(pointer, whatfor);
140 c=realloc(*(int **)pointer, (size_t)size*(size_t)howmany);
141 malloced_bytes+=size*howmany;
142 if (c==0) memory_out_error(size, howmany, whatfor);
144 printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \
145 for %s was (%08lx) now (%08lx)\n",
146 ((long int)size) * ((long int)howmany),
147 (long int)howmany,(long int)size,whatfor,
148 (long int) *(int **)pointer, (long int) c);
149 *(int **)pointer = c;
154 extern void my_free(void *pointer, char *whatitwas)
156 if (*(int **)pointer != NULL)
158 printf("Freeing memory for %s at (%08lx)\n",
159 whatitwas, (long int) (*(int **)pointer));
161 hfree(*(int **)pointer);
163 free(*(int **)pointer);
165 *(int **)pointer = NULL;
169 /* ------------------------------------------------------------------------- */
170 /* Extensible blocks of memory, providing a kind of RAM disc as an */
171 /* alternative to the temporary files option */
173 /* The allocation is slightly confusing. A block can store up to 72 */
174 /* chunks, which are allocated as needed when data is written. (Data does */
175 /* not have to be written in order, but you should not try to read a byte */
176 /* before writing it.) The size of a chunk is defined by ALLOC_CHUNK_SIZE. */
177 /* So any block can store any amount of data, but you increase the limit */
178 /* (for all blocks) by increasing ALLOC_CHUNK_SIZE, not the number of */
180 /* ------------------------------------------------------------------------- */
182 static char chunk_name_buffer[60];
183 static char *chunk_name(memory_block *MB, int no)
184 { char *p = "(unknown)";
185 if (MB == &static_strings_area) p = "static strings area";
186 if (MB == &zcode_area) p = "Z-code area";
187 if (MB == &link_data_area) p = "link data area";
188 if (MB == &zcode_backpatch_table) p = "Z-code backpatch table";
189 if (MB == &staticarray_backpatch_table) p = "Static array backpatch table";
190 if (MB == &zmachine_backpatch_table) p = "Z-machine backpatch table";
191 sprintf(chunk_name_buffer, "%s chunk %d", p, no);
192 return(chunk_name_buffer);
195 extern void initialise_memory_block(memory_block *MB)
198 for (i=0; i<72; i++) MB->chunk[i] = NULL;
199 MB->extent_of_last = 0;
203 extern void deallocate_memory_block(memory_block *MB)
206 if (MB->chunk[i] != NULL)
207 my_free(&(MB->chunk[i]), chunk_name(MB, i));
209 MB->extent_of_last = 0;
212 extern int read_byte_from_memory_block(memory_block *MB, int32 index)
214 p = MB->chunk[index/ALLOC_CHUNK_SIZE];
216 { compiler_error_named("memory: read from unwritten byte in",
217 chunk_name(MB, index/ALLOC_CHUNK_SIZE));
220 return p[index % ALLOC_CHUNK_SIZE];
223 extern void write_byte_to_memory_block(memory_block *MB, int32 index, int value)
224 { uchar *p; int ch = index/ALLOC_CHUNK_SIZE;
226 { compiler_error_named("memory: negative index to", chunk_name(MB, 0));
229 if (ch >= 72) memoryerror("ALLOC_CHUNK_SIZE", ALLOC_CHUNK_SIZE);
231 if (MB->chunk[ch] == NULL)
233 MB->chunk[ch] = my_malloc(ALLOC_CHUNK_SIZE, chunk_name(MB, ch));
235 for (i=0; i<ALLOC_CHUNK_SIZE; i++) p[i] = 255;
239 p[index % ALLOC_CHUNK_SIZE] = value;
242 /* ------------------------------------------------------------------------- */
243 /* Where the memory settings are declared as variables */
244 /* ------------------------------------------------------------------------- */
248 int SYMBOLS_CHUNK_SIZE;
254 int MAX_DICT_ENTRIES;
256 int MAX_PROP_TABLE_SIZE;
258 int MAX_DYNAMIC_STRINGS;
259 int MAX_EXPRESSION_NODES;
264 int32 MAX_STATIC_STRINGS;
265 int32 MAX_ZCODE_SIZE;
267 int32 MAX_TRANSCRIPT_SIZE;
269 int32 MAX_LINK_DATA_SIZE;
270 int MAX_INCLUSION_DEPTH;
271 int MAX_SOURCE_FILES;
272 int32 MAX_INDIV_PROP_TABLE_SIZE;
273 int32 MAX_OBJ_PROP_TABLE_SIZE;
274 int MAX_OBJ_PROP_COUNT;
275 int MAX_LOCAL_VARIABLES;
276 int MAX_GLOBAL_VARIABLES;
277 int DICT_WORD_SIZE; /* number of characters in a dict word */
278 int DICT_CHAR_SIZE; /* (glulx) 1 for one-byte chars, 4 for Unicode chars */
279 int DICT_WORD_BYTES; /* DICT_WORD_SIZE*DICT_CHAR_SIZE */
280 int ZCODE_HEADER_EXT_WORDS; /* (zcode 1.0) requested header extension size */
281 int ZCODE_HEADER_FLAGS_3; /* (zcode 1.1) value to place in Flags 3 word */
283 int GLULX_OBJECT_EXT_BYTES; /* (glulx) extra bytes for each object record */
284 int32 MAX_NUM_STATIC_STRINGS;
285 int32 MAX_UNICODE_CHARS;
286 int32 MAX_STACK_SIZE;
287 int32 MEMORY_MAP_EXTENSION;
288 int ALLOC_CHUNK_SIZE;
289 int WARN_UNUSED_ROUTINES; /* 0: no, 1: yes except in system files, 2: yes always */
290 int OMIT_UNUSED_ROUTINES; /* 0: no, 1: yes */
291 int TRANSCRIPT_FORMAT; /* 0: classic, 1: prefixed */
293 /* The way memory sizes are set causes great nuisance for those parameters
294 which have different defaults under Z-code and Glulx. We have to get
295 the defaults right whether the user sets "-G $HUGE" or "$HUGE -G".
296 And an explicit value set by the user should override both defaults. */
297 static int32 MAX_ZCODE_SIZE_z, MAX_ZCODE_SIZE_g;
298 static int MAX_PROP_TABLE_SIZE_z, MAX_PROP_TABLE_SIZE_g;
299 static int MAX_GLOBAL_VARIABLES_z, MAX_GLOBAL_VARIABLES_g;
300 static int MAX_LOCAL_VARIABLES_z, MAX_LOCAL_VARIABLES_g;
301 static int DICT_WORD_SIZE_z, DICT_WORD_SIZE_g;
302 static int NUM_ATTR_BYTES_z, NUM_ATTR_BYTES_g;
303 static int ALLOC_CHUNK_SIZE_z, ALLOC_CHUNK_SIZE_g;
304 static int MAX_DYNAMIC_STRINGS_z, MAX_DYNAMIC_STRINGS_g;
306 /* ------------------------------------------------------------------------- */
307 /* Memory control from the command line */
308 /* ------------------------------------------------------------------------- */
310 static void list_memory_sizes(void)
311 { printf("+--------------------------------------+\n");
312 printf("| %25s = %-7s |\n","Memory setting","Value");
313 printf("+--------------------------------------+\n");
314 printf("| %25s = %-7d |\n","MAX_ABBREVS",MAX_ABBREVS);
315 printf("| %25s = %-7d |\n","MAX_ACTIONS",MAX_ACTIONS);
316 printf("| %25s = %-7d |\n","MAX_ADJECTIVES",MAX_ADJECTIVES);
317 printf("| %25s = %-7d |\n","ALLOC_CHUNK_SIZE",ALLOC_CHUNK_SIZE);
318 printf("| %25s = %-7d |\n","MAX_ARRAYS",MAX_ARRAYS);
319 printf("| %25s = %-7d |\n","NUM_ATTR_BYTES",NUM_ATTR_BYTES);
320 printf("| %25s = %-7d |\n","MAX_CLASSES",MAX_CLASSES);
321 printf("| %25s = %-7d |\n","MAX_DICT_ENTRIES",MAX_DICT_ENTRIES);
322 printf("| %25s = %-7d |\n","DICT_WORD_SIZE",DICT_WORD_SIZE);
324 printf("| %25s = %-7d |\n","DICT_CHAR_SIZE",DICT_CHAR_SIZE);
325 printf("| %25s = %-7d |\n","MAX_DYNAMIC_STRINGS",MAX_DYNAMIC_STRINGS);
326 printf("| %25s = %-7d |\n","MAX_EXPRESSION_NODES",MAX_EXPRESSION_NODES);
327 printf("| %25s = %-7d |\n","MAX_GLOBAL_VARIABLES",MAX_GLOBAL_VARIABLES);
328 printf("| %25s = %-7d |\n","HASH_TAB_SIZE",HASH_TAB_SIZE);
330 printf("| %25s = %-7d |\n","ZCODE_HEADER_EXT_WORDS",ZCODE_HEADER_EXT_WORDS);
332 printf("| %25s = %-7d |\n","ZCODE_HEADER_FLAGS_3",ZCODE_HEADER_FLAGS_3);
333 printf("| %25s = %-7d |\n","MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH);
334 printf("| %25s = %-7d |\n","MAX_INDIV_PROP_TABLE_SIZE", MAX_INDIV_PROP_TABLE_SIZE);
335 printf("| %25s = %-7d |\n","INDIV_PROP_START", INDIV_PROP_START);
336 printf("| %25s = %-7d |\n","MAX_LABELS",MAX_LABELS);
337 printf("| %25s = %-7d |\n","MAX_LINESPACE",MAX_LINESPACE);
338 printf("| %25s = %-7d |\n","MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE);
340 printf("| %25s = %-7d |\n","MAX_LOCAL_VARIABLES",MAX_LOCAL_VARIABLES);
341 printf("| %25s = %-7d |\n","MAX_LOW_STRINGS",MAX_LOW_STRINGS);
343 printf("| %25s = %-7d |\n","MEMORY_MAP_EXTENSION",
344 MEMORY_MAP_EXTENSION);
346 printf("| %25s = %-7d |\n","MAX_NUM_STATIC_STRINGS",
347 MAX_NUM_STATIC_STRINGS);
348 printf("| %25s = %-7d |\n","MAX_OBJECTS",MAX_OBJECTS);
350 printf("| %25s = %-7d |\n","GLULX_OBJECT_EXT_BYTES",
351 GLULX_OBJECT_EXT_BYTES);
353 printf("| %25s = %-7d |\n","MAX_OBJ_PROP_COUNT",
356 printf("| %25s = %-7d |\n","MAX_OBJ_PROP_TABLE_SIZE",
357 MAX_OBJ_PROP_TABLE_SIZE);
358 printf("| %25s = %-7d |\n","MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
359 printf("| %25s = %-7d |\n","MAX_QTEXT_SIZE",MAX_QTEXT_SIZE);
360 printf("| %25s = %-7d |\n","MAX_SOURCE_FILES",MAX_SOURCE_FILES);
362 printf("| %25s = %-7ld |\n","MAX_STACK_SIZE",
363 (long int) MAX_STACK_SIZE);
364 printf("| %25s = %-7d |\n","MAX_STATIC_DATA",MAX_STATIC_DATA);
365 printf("| %25s = %-7ld |\n","MAX_STATIC_STRINGS",
366 (long int) MAX_STATIC_STRINGS);
367 printf("| %25s = %-7d |\n","MAX_SYMBOLS",MAX_SYMBOLS);
368 printf("| %25s = %-7d |\n","SYMBOLS_CHUNK_SIZE",SYMBOLS_CHUNK_SIZE);
369 printf("| %25s = %-7d |\n","TRANSCRIPT_FORMAT",TRANSCRIPT_FORMAT);
370 printf("| %25s = %-7ld |\n","MAX_TRANSCRIPT_SIZE",
371 (long int) MAX_TRANSCRIPT_SIZE);
373 printf("| %25s = %-7ld |\n","MAX_UNICODE_CHARS",
374 (long int) MAX_UNICODE_CHARS);
375 printf("| %25s = %-7d |\n","WARN_UNUSED_ROUTINES",WARN_UNUSED_ROUTINES);
376 printf("| %25s = %-7d |\n","OMIT_UNUSED_ROUTINES",OMIT_UNUSED_ROUTINES);
377 printf("| %25s = %-7d |\n","MAX_VERBS",MAX_VERBS);
378 printf("| %25s = %-7d |\n","MAX_VERBSPACE",MAX_VERBSPACE);
379 printf("| %25s = %-7ld |\n","MAX_ZCODE_SIZE",
380 (long int) MAX_ZCODE_SIZE);
381 printf("+--------------------------------------+\n");
384 extern void set_memory_sizes(int size_flag)
386 if (size_flag == HUGE_SIZE)
388 MAX_QTEXT_SIZE = 4000;
391 SYMBOLS_CHUNK_SIZE = 5000;
398 MAX_DICT_ENTRIES = 2000;
399 MAX_STATIC_DATA = 10000;
401 MAX_PROP_TABLE_SIZE_z = 30000;
402 MAX_PROP_TABLE_SIZE_g = 60000;
404 MAX_EXPRESSION_NODES = 100;
406 MAX_VERBSPACE = 4096;
408 MAX_LINESPACE = 16000;
410 MAX_STATIC_STRINGS = 8000;
411 MAX_ZCODE_SIZE_z = 20000;
412 MAX_ZCODE_SIZE_g = 40000;
413 MAX_LINK_DATA_SIZE = 2000;
415 MAX_LOW_STRINGS = 2048;
417 MAX_TRANSCRIPT_SIZE = 200000;
418 MAX_NUM_STATIC_STRINGS = 20000;
422 MAX_OBJ_PROP_COUNT = 128;
423 MAX_OBJ_PROP_TABLE_SIZE = 4096;
425 MAX_INDIV_PROP_TABLE_SIZE = 15000;
428 MAX_GLOBAL_VARIABLES_z = 240;
429 MAX_GLOBAL_VARIABLES_g = 512;
431 ALLOC_CHUNK_SIZE_z = 8192;
432 ALLOC_CHUNK_SIZE_g = 32768;
434 if (size_flag == LARGE_SIZE)
436 MAX_QTEXT_SIZE = 4000;
439 SYMBOLS_CHUNK_SIZE = 5000;
446 MAX_DICT_ENTRIES = 1300;
447 MAX_STATIC_DATA = 10000;
449 MAX_PROP_TABLE_SIZE_z = 15000;
450 MAX_PROP_TABLE_SIZE_g = 30000;
452 MAX_EXPRESSION_NODES = 100;
454 MAX_VERBSPACE = 4096;
455 MAX_LINESPACE = 10000;
458 MAX_STATIC_STRINGS = 8000;
459 MAX_ZCODE_SIZE_z = 20000;
460 MAX_ZCODE_SIZE_g = 40000;
461 MAX_LINK_DATA_SIZE = 2000;
463 MAX_LOW_STRINGS = 2048;
465 MAX_TRANSCRIPT_SIZE = 200000;
466 MAX_NUM_STATIC_STRINGS = 20000;
470 MAX_OBJ_PROP_COUNT = 64;
471 MAX_OBJ_PROP_TABLE_SIZE = 2048;
473 MAX_INDIV_PROP_TABLE_SIZE = 10000;
476 MAX_GLOBAL_VARIABLES_z = 240;
477 MAX_GLOBAL_VARIABLES_g = 512;
479 ALLOC_CHUNK_SIZE_z = 8192;
480 ALLOC_CHUNK_SIZE_g = 16384;
482 if (size_flag == SMALL_SIZE)
484 MAX_QTEXT_SIZE = 4000;
487 SYMBOLS_CHUNK_SIZE = 2500;
494 MAX_DICT_ENTRIES = 700;
495 MAX_STATIC_DATA = 10000;
497 MAX_PROP_TABLE_SIZE_z = 8000;
498 MAX_PROP_TABLE_SIZE_g = 16000;
500 MAX_EXPRESSION_NODES = 40;
502 MAX_VERBSPACE = 2048;
503 MAX_LINESPACE = 10000;
506 MAX_STATIC_STRINGS = 8000;
507 MAX_ZCODE_SIZE_z = 10000;
508 MAX_ZCODE_SIZE_g = 20000;
509 MAX_LINK_DATA_SIZE = 1000;
511 MAX_LOW_STRINGS = 1024;
513 MAX_TRANSCRIPT_SIZE = 100000;
514 MAX_NUM_STATIC_STRINGS = 10000;
518 MAX_OBJ_PROP_COUNT = 64;
519 MAX_OBJ_PROP_TABLE_SIZE = 1024;
521 MAX_INDIV_PROP_TABLE_SIZE = 5000;
524 MAX_GLOBAL_VARIABLES_z = 240;
525 MAX_GLOBAL_VARIABLES_g = 256;
527 ALLOC_CHUNK_SIZE_z = 8192;
528 ALLOC_CHUNK_SIZE_g = 8192;
531 /* Regardless of size_flag... */
532 MAX_SOURCE_FILES = 256;
533 MAX_INCLUSION_DEPTH = 5;
534 MAX_LOCAL_VARIABLES_z = 16;
535 MAX_LOCAL_VARIABLES_g = 32;
537 DICT_WORD_SIZE_z = 6;
538 DICT_WORD_SIZE_g = 9;
539 NUM_ATTR_BYTES_z = 6;
540 NUM_ATTR_BYTES_g = 7;
542 MAX_DYNAMIC_STRINGS_z = 32;
543 MAX_DYNAMIC_STRINGS_g = 64;
544 /* Backwards-compatible behavior: allow for a unicode table
545 whether we need one or not. The user can set this to zero if
546 there's no unicode table. */
547 ZCODE_HEADER_EXT_WORDS = 3;
548 ZCODE_HEADER_FLAGS_3 = 0;
549 GLULX_OBJECT_EXT_BYTES = 0;
550 MAX_UNICODE_CHARS = 64;
551 MEMORY_MAP_EXTENSION = 0;
552 /* We estimate the default Glulx stack size at 4096. That's about
553 enough for 90 nested function calls with 8 locals each -- the
554 same capacity as the Z-Spec's suggestion for Z-machine stack
555 size. Note that Inform 7 wants more stack; I7-generated code
556 sets MAX_STACK_SIZE to 65536 by default. */
557 MAX_STACK_SIZE = 4096;
558 OMIT_UNUSED_ROUTINES = 0;
559 WARN_UNUSED_ROUTINES = 0;
560 TRANSCRIPT_FORMAT = 0;
562 adjust_memory_sizes();
565 extern void adjust_memory_sizes()
568 MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_z;
569 MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_z;
570 MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_z;
571 MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_z;
572 DICT_WORD_SIZE = DICT_WORD_SIZE_z;
573 NUM_ATTR_BYTES = NUM_ATTR_BYTES_z;
574 ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_z;
575 MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_z;
576 INDIV_PROP_START = 64;
579 MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_g;
580 MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_g;
581 MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_g;
582 MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_g;
583 DICT_WORD_SIZE = DICT_WORD_SIZE_g;
584 NUM_ATTR_BYTES = NUM_ATTR_BYTES_g;
585 ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_g;
586 MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_g;
587 INDIV_PROP_START = 256;
591 static void explain_parameter(char *command)
593 if (strcmp(command,"MAX_QTEXT_SIZE")==0)
595 " MAX_QTEXT_SIZE is the maximum length of a quoted string. Increasing\n\
596 by 1 costs 5 bytes (for lexical analysis memory). Inform automatically\n\
597 ensures that MAX_STATIC_STRINGS is at least twice the size of this.");
600 if (strcmp(command,"MAX_SYMBOLS")==0)
602 " MAX_SYMBOLS is the maximum number of symbols - names of variables, \n\
603 objects, routines, the many internal Inform-generated names and so on.\n");
606 if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0)
608 " The symbols names are stored in memory which is allocated in chunks \n\
609 of size SYMBOLS_CHUNK_SIZE.\n");
612 if (strcmp(command,"HASH_TAB_SIZE")==0)
614 " HASH_TAB_SIZE is the size of the hash tables used for the heaviest \n\
618 if (strcmp(command,"MAX_OBJECTS")==0)
620 " MAX_OBJECTS is the maximum number of objects. (If compiling a version-3 \n\
621 game, 255 is an absolute maximum in any event.)\n");
624 if (strcmp(command,"MAX_ACTIONS")==0)
626 " MAX_ACTIONS is the maximum number of actions - that is, routines such as \n\
627 TakeSub which are referenced in the grammar table.\n");
630 if (strcmp(command,"MAX_ADJECTIVES")==0)
632 " MAX_ADJECTIVES is the maximum number of different \"adjectives\" in the \n\
633 grammar table. Adjectives are misleadingly named: they are words such as \n\
634 \"in\", \"under\" and the like.\n");
637 if (strcmp(command,"MAX_DICT_ENTRIES")==0)
639 " MAX_DICT_ENTRIES is the maximum number of words which can be entered \n\
640 into the game's dictionary. It costs 29 bytes to increase this by one.\n");
643 if (strcmp(command,"DICT_WORD_SIZE")==0)
645 " DICT_WORD_SIZE is the number of characters in a dictionary word. In \n\
646 Z-code this is always 6 (only 4 are used in v3 games). In Glulx it \n\
647 can be any number.\n");
650 if (strcmp(command,"DICT_CHAR_SIZE")==0)
652 " DICT_CHAR_SIZE is the byte size of one character in the dictionary. \n\
653 (This is only meaningful in Glulx, since Z-code has compressed dictionary \n\
654 words.) It can be either 1 (the default) or 4 (to enable full Unicode \n\
658 if (strcmp(command,"NUM_ATTR_BYTES")==0)
660 " NUM_ATTR_BYTES is the space used to store attribute flags. Each byte \n\
661 stores eight attributes. In Z-code this is always 6 (only 4 are used in \n\
662 v3 games). In Glulx it can be any number which is a multiple of four, \n\
666 if (strcmp(command,"ZCODE_HEADER_EXT_WORDS")==0)
668 " ZCODE_HEADER_EXT_WORDS is the number of words in the Z-code header \n\
669 extension table (Z-Spec 1.0). The -W switch also sets this. It defaults \n\
670 to 3, but can be set higher. (It can be set lower if no Unicode \n\
671 translation table is created.)\n");
674 if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0)
676 " ZCODE_HEADER_FLAGS_3 is the value to store in the Flags 3 word of the \n\
677 header extension table (Z-Spec 1.1).\n");
680 if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
682 " GLULX_OBJECT_EXT_BYTES is an amount of additional space to add to each \n\
683 object record. It is initialized to zero bytes, and the game is free to \n\
684 use it as desired. (This is only meaningful in Glulx, since Z-code \n\
685 specifies the object structure.)\n");
688 if (strcmp(command,"MAX_STATIC_DATA")==0)
690 " MAX_STATIC_DATA is the size of an array of integers holding initial \n\
691 values for arrays and strings stored as ASCII inside the Z-machine. It \n\
692 should be at least 1024 but seldom needs much more.\n");
695 if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0)
697 " MAX_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\
698 properties table.\n");
701 if (strcmp(command,"MAX_ABBREVS")==0)
703 " MAX_ABBREVS is the maximum number of declared abbreviations. It is not \n\
704 allowed to exceed 96 in Z-code.\n");
707 if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0)
709 " MAX_DYNAMIC_STRINGS is the maximum number of string substitution variables\n\
710 (\"@00\"). It is not allowed to exceed 96 in Z-code or 100 in Glulx.\n");
713 if (strcmp(command,"MAX_ARRAYS")==0)
715 " MAX_ARRAYS is the maximum number of declared arrays.\n");
718 if (strcmp(command,"MAX_EXPRESSION_NODES")==0)
720 " MAX_EXPRESSION_NODES is the maximum number of nodes in the expression \n\
721 evaluator's storage for parse trees. In effect, it measures how \n\
722 complicated algebraic expressions are allowed to be. Increasing it by \n\
723 one costs about 80 bytes.\n");
726 if (strcmp(command,"MAX_VERBS")==0)
728 " MAX_VERBS is the maximum number of verbs (such as \"take\") which can be \n\
729 defined, each with its own grammar. To increase it by one costs about\n\
730 128 bytes. A full game will contain at least 100.\n");
733 if (strcmp(command,"MAX_VERBSPACE")==0)
735 " MAX_VERBSPACE is the size of workspace used to store verb words, so may\n\
736 need increasing in games with many synonyms: unlikely to exceed 4K.\n");
739 if (strcmp(command,"MAX_LABELS")==0)
741 " MAX_LABELS is the maximum number of label points in any one routine.\n\
742 (If the -k debugging information switch is set, MAX_LABELS is raised to\n\
743 a minimum level of 2000, as about twice the normal number of label points\n\
744 are needed to generate tables of how source code corresponds to positions\n\
745 in compiled code.)");
748 if (strcmp(command,"MAX_LINESPACE")==0)
750 " MAX_LINESPACE is the size of workspace used to store grammar lines, so \n\
751 may need increasing in games with complex or extensive grammars.\n");
754 if (strcmp(command,"MAX_STATIC_STRINGS")==0)
757 " MAX_STATIC_STRINGS is the size in bytes of a buffer to hold compiled\n\
758 strings before they're written into longer-term storage. 2000 bytes is \n\
759 plenty, allowing string constants of up to about 3000 characters long.\n\
760 Inform automatically ensures that this is at least twice the size of\n\
761 MAX_QTEXT_SIZE, to be on the safe side.");
764 if (strcmp(command,"MAX_ZCODE_SIZE")==0)
767 " MAX_ZCODE_SIZE is the size in bytes of a buffer to hold compiled \n\
768 code for a single routine. (It applies to both Z-code and Glulx, \n\
769 despite the name.) As a guide, the longest library routine is \n\
770 about 6500 bytes long in Z-code; about twice that in Glulx.");
773 if (strcmp(command,"MAX_LINK_DATA_SIZE")==0)
776 " MAX_LINK_DATA_SIZE is the size in bytes of a buffer to hold module \n\
777 link data before it's written into longer-term storage. 2000 bytes \n\
781 if (strcmp(command,"MAX_LOW_STRINGS")==0)
783 " MAX_LOW_STRINGS is the size in bytes of a buffer to hold all the \n\
784 compiled \"low strings\" which are to be written above the synonyms table \n\
785 in the Z-machine. 1024 is plenty.\n");
788 if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0)
790 " MAX_TRANSCRIPT_SIZE is only allocated for the abbreviations optimisation \n\
791 switch, and has the size in bytes of a buffer to hold the entire text of\n\
792 the game being compiled: it has to be enormous, say 100000 to 200000.\n");
795 if (strcmp(command,"MAX_CLASSES")==0)
797 " MAX_CLASSES maximum number of object classes which can be defined. This\n\
798 is cheap to increase.\n");
801 if (strcmp(command,"MAX_INCLUSION_DEPTH")==0)
803 " MAX_INCLUSION_DEPTH is the number of nested includes permitted.\n");
806 if (strcmp(command,"MAX_SOURCE_FILES")==0)
808 " MAX_SOURCE_FILES is the number of source files that can be read in the \n\
812 if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0)
814 " MAX_INDIV_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\
815 table of ..variable values.\n");
818 if (strcmp(command,"INDIV_PROP_START")==0)
820 " Properties 1 to INDIV_PROP_START-1 are common properties; individual\n\
821 properties are numbered INDIV_PROP_START and up.\n");
824 if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0)
826 " MAX_OBJ_PROP_COUNT is the maximum number of properties a single object \n\
827 can have. (Glulx only)\n");
830 if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0)
832 " MAX_OBJ_PROP_TABLE_SIZE is the number of words allocated to hold a \n\
833 single object's properties. (Glulx only)\n");
836 if (strcmp(command,"MAX_LOCAL_VARIABLES")==0)
838 " MAX_LOCAL_VARIABLES is the number of local variables (including \n\
839 arguments) allowed in a procedure. (Glulx only)\n");
842 if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
844 " MAX_GLOBAL_VARIABLES is the number of global variables allowed in the \n\
845 program. (Glulx only)\n");
848 if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
851 " MAX_NUM_STATIC_STRINGS is the maximum number of compiled strings \n\
852 allowed in the program. (Glulx only)\n");
855 if (strcmp(command,"MAX_UNICODE_CHARS")==0)
858 " MAX_UNICODE_CHARS is the maximum number of different Unicode characters \n\
859 (beyond the Latin-1 range, $00..$FF) which the game text can use. \n\
863 if (strcmp(command,"ALLOC_CHUNK_SIZE")==0)
866 " ALLOC_CHUNK_SIZE is a base unit of Inform's internal memory allocation \n\
867 for various structures.\n");
870 if (strcmp(command,"MAX_STACK_SIZE")==0)
873 " MAX_STACK_SIZE is the maximum size (in bytes) of the interpreter stack \n\
874 during gameplay. (Glulx only)\n");
877 if (strcmp(command,"MEMORY_MAP_EXTENSION")==0)
880 " MEMORY_MAP_EXTENSION is the number of bytes (all zeroes) to map into \n\
881 memory after the game file. (Glulx only)\n");
884 if (strcmp(command,"TRANSCRIPT_FORMAT")==0)
887 " TRANSCRIPT_FORMAT, if set to 1, adjusts the gametext.txt transcript for \n\
888 easier machine processing; each line will be prefixed by its context.\n");
891 if (strcmp(command,"WARN_UNUSED_ROUTINES")==0)
894 " WARN_UNUSED_ROUTINES, if set to 2, will display a warning for each \n\
895 routine in the game file which is never called. (This includes \n\
896 routines called only from uncalled routines, etc.) If set to 1, will warn \n\
897 only about functions in game code, not in the system library.\n");
900 if (strcmp(command,"OMIT_UNUSED_ROUTINES")==0)
903 " OMIT_UNUSED_ROUTINES, if set to 1, will avoid compiling unused routines \n\
904 into the game file.\n");
907 if (strcmp(command,"SERIAL")==0)
910 " SERIAL, if set, will be used as the six digit serial number written into \n\
911 the header of the output file.\n");
915 printf("No such memory setting as \"%s\"\n",command);
920 /* Parse a decimal number as an int32. Return true if a valid number
921 was found; otherwise print a warning and return false.
923 Anything over nine digits is considered an overflow; we report a
924 warning but return +/- 999999999 (and true). This is not entirely
925 clever about leading zeroes ("0000000001" is treated as an
926 overflow) but this is better than trying to detect genuine
929 (Some Glulx settings might conceivably want to go up to $7FFFFFFF,
930 which is a ten-digit number, but we're not going to allow that
933 This used to rely on atoi(), and we retain the atoi() behavior of
934 ignoring garbage characters after a valid decimal number.
936 static int parse_memory_setting(char *str, char *label, int32 *result)
944 while (*cx == ' ') cx++;
946 val = strtol(cx, &ex, 10);
949 printf("Bad numerical setting in $ command \"%s=%s\"\n",
957 printf("Numerical setting underflowed in $ command \"%s=%s\" (limiting to %ld)\n",
964 printf("Numerical setting overflowed in $ command \"%s=%s\" (limiting to %ld)\n",
969 *result = (int32)val;
973 static void add_predefined_symbol(char *command)
980 for (ix=0; command[ix]; ix++) {
981 if (command[ix] == '=') {
982 valpos = command+(ix+1);
988 for (ix=0; command[ix]; ix++) {
989 if ((ix == 0 && isdigit(command[ix]))
990 || !(isalnum(command[ix]) || command[ix] == '_')) {
991 printf("Attempt to define invalid symbol: %s\n", command);
997 if (!parse_memory_setting(valpos, command, &value)) {
1002 add_config_symbol_definition(command, value);
1005 /* Handle a dollar-sign command option: $LIST, $FOO=VAL, and so on.
1006 The option may come from the command line, an ICL file, or a header
1009 (Unix-style command-line options are converted to dollar-sign format
1010 before being sent here.)
1012 The name of this function is outdated. Many of these settings are not
1013 really about memory allocation.
1015 extern void memory_command(char *command)
1016 { int i, k, flag=0; int32 j;
1018 for (k=0; command[k]!=0; k++)
1019 if (islower(command[k])) command[k]=toupper(command[k]);
1021 if (command[0]=='?') { explain_parameter(command+1); return; }
1022 if (command[0]=='#') { add_predefined_symbol(command+1); return; }
1024 if (strcmp(command, "HUGE")==0) { set_memory_sizes(HUGE_SIZE); return; }
1025 if (strcmp(command, "LARGE")==0) { set_memory_sizes(LARGE_SIZE); return; }
1026 if (strcmp(command, "SMALL")==0) { set_memory_sizes(SMALL_SIZE); return; }
1027 if (strcmp(command, "LIST")==0) { list_memory_sizes(); return; }
1028 for (i=0; command[i]!=0; i++)
1029 { if (command[i]=='=')
1031 if (!parse_memory_setting(command+i+1, command, &j)) {
1034 if (strcmp(command,"BUFFER_LENGTH")==0)
1036 if (strcmp(command,"MAX_QTEXT_SIZE")==0)
1037 { MAX_QTEXT_SIZE=j, flag=1;
1038 if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS)
1039 MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE;
1041 if (strcmp(command,"MAX_SYMBOLS")==0)
1042 MAX_SYMBOLS=j, flag=1;
1043 if (strcmp(command,"MAX_BANK_SIZE")==0)
1045 if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0)
1046 SYMBOLS_CHUNK_SIZE=j, flag=1;
1047 if (strcmp(command,"BANK_CHUNK_SIZE")==0)
1049 if (strcmp(command,"HASH_TAB_SIZE")==0)
1050 HASH_TAB_SIZE=j, flag=1;
1051 if (strcmp(command,"MAX_OBJECTS")==0)
1052 MAX_OBJECTS=j, flag=1;
1053 if (strcmp(command,"MAX_ACTIONS")==0)
1054 MAX_ACTIONS=j, flag=1;
1055 if (strcmp(command,"MAX_ADJECTIVES")==0)
1056 MAX_ADJECTIVES=j, flag=1;
1057 if (strcmp(command,"MAX_DICT_ENTRIES")==0)
1058 MAX_DICT_ENTRIES=j, flag=1;
1059 if (strcmp(command,"DICT_WORD_SIZE")==0)
1060 { DICT_WORD_SIZE=j, flag=1;
1061 DICT_WORD_SIZE_g=DICT_WORD_SIZE_z=j;
1063 if (strcmp(command,"DICT_CHAR_SIZE")==0)
1064 DICT_CHAR_SIZE=j, flag=1;
1065 if (strcmp(command,"NUM_ATTR_BYTES")==0)
1066 { NUM_ATTR_BYTES=j, flag=1;
1067 NUM_ATTR_BYTES_g=NUM_ATTR_BYTES_z=j;
1069 if (strcmp(command,"ZCODE_HEADER_EXT_WORDS")==0)
1070 ZCODE_HEADER_EXT_WORDS=j, flag=1;
1071 if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0)
1072 ZCODE_HEADER_FLAGS_3=j, flag=1;
1073 if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
1074 GLULX_OBJECT_EXT_BYTES=j, flag=1;
1075 if (strcmp(command,"MAX_STATIC_DATA")==0)
1076 MAX_STATIC_DATA=j, flag=1;
1077 if (strcmp(command,"MAX_OLDEPTH")==0)
1079 if (strcmp(command,"MAX_ROUTINES")==0)
1081 if (strcmp(command,"MAX_GCONSTANTS")==0)
1083 if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0)
1084 { MAX_PROP_TABLE_SIZE=j, flag=1;
1085 MAX_PROP_TABLE_SIZE_g=MAX_PROP_TABLE_SIZE_z=j;
1087 if (strcmp(command,"MAX_FORWARD_REFS")==0)
1089 if (strcmp(command,"STACK_SIZE")==0)
1091 if (strcmp(command,"STACK_LONG_SLOTS")==0)
1093 if (strcmp(command,"STACK_SHORT_LENGTH")==0)
1095 if (strcmp(command,"MAX_ABBREVS")==0)
1096 MAX_ABBREVS=j, flag=1;
1097 if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0)
1098 { MAX_DYNAMIC_STRINGS=j, flag=1;
1099 MAX_DYNAMIC_STRINGS_g=MAX_DYNAMIC_STRINGS_z=j;
1101 if (strcmp(command,"MAX_ARRAYS")==0)
1102 MAX_ARRAYS=j, flag=1;
1103 if (strcmp(command,"MAX_EXPRESSION_NODES")==0)
1104 MAX_EXPRESSION_NODES=j, flag=1;
1105 if (strcmp(command,"MAX_VERBS")==0)
1106 MAX_VERBS=j, flag=1;
1107 if (strcmp(command,"MAX_VERBSPACE")==0)
1108 MAX_VERBSPACE=j, flag=1;
1109 if (strcmp(command,"MAX_LABELS")==0)
1110 MAX_LABELS=j, flag=1;
1111 if (strcmp(command,"MAX_LINESPACE")==0)
1112 MAX_LINESPACE=j, flag=1;
1113 if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
1114 MAX_NUM_STATIC_STRINGS=j, flag=1;
1115 if (strcmp(command,"MAX_STATIC_STRINGS")==0)
1116 { MAX_STATIC_STRINGS=j, flag=1;
1117 if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS)
1118 MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE;
1120 if (strcmp(command,"MAX_ZCODE_SIZE")==0)
1121 { MAX_ZCODE_SIZE=j, flag=1;
1122 MAX_ZCODE_SIZE_g=MAX_ZCODE_SIZE_z=j;
1124 if (strcmp(command,"MAX_LINK_DATA_SIZE")==0)
1125 MAX_LINK_DATA_SIZE=j, flag=1;
1126 if (strcmp(command,"MAX_LOW_STRINGS")==0)
1127 MAX_LOW_STRINGS=j, flag=1;
1128 if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0)
1129 MAX_TRANSCRIPT_SIZE=j, flag=1;
1130 if (strcmp(command,"MAX_CLASSES")==0)
1131 MAX_CLASSES=j, flag=1;
1132 if (strcmp(command,"MAX_INCLUSION_DEPTH")==0)
1133 MAX_INCLUSION_DEPTH=j, flag=1;
1134 if (strcmp(command,"MAX_SOURCE_FILES")==0)
1135 MAX_SOURCE_FILES=j, flag=1;
1136 if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0)
1137 MAX_INDIV_PROP_TABLE_SIZE=j, flag=1;
1138 if (strcmp(command,"INDIV_PROP_START")==0)
1139 INDIV_PROP_START=j, flag=1;
1140 if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0)
1141 MAX_OBJ_PROP_TABLE_SIZE=j, flag=1;
1142 if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0)
1143 MAX_OBJ_PROP_COUNT=j, flag=1;
1144 if (strcmp(command,"MAX_LOCAL_VARIABLES")==0)
1145 { MAX_LOCAL_VARIABLES=j, flag=1;
1146 MAX_LOCAL_VARIABLES_g=MAX_LOCAL_VARIABLES_z=j;
1148 if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
1149 { MAX_GLOBAL_VARIABLES=j, flag=1;
1150 MAX_GLOBAL_VARIABLES_g=MAX_GLOBAL_VARIABLES_z=j;
1152 if (strcmp(command,"ALLOC_CHUNK_SIZE")==0)
1153 { ALLOC_CHUNK_SIZE=j, flag=1;
1154 ALLOC_CHUNK_SIZE_g=ALLOC_CHUNK_SIZE_z=j;
1156 if (strcmp(command,"MAX_UNICODE_CHARS")==0)
1157 MAX_UNICODE_CHARS=j, flag=1;
1158 if (strcmp(command,"MAX_STACK_SIZE")==0)
1160 MAX_STACK_SIZE=j, flag=1;
1161 /* Adjust up to a 256-byte boundary. */
1162 MAX_STACK_SIZE = (MAX_STACK_SIZE + 0xFF) & (~0xFF);
1164 if (strcmp(command,"MEMORY_MAP_EXTENSION")==0)
1166 MEMORY_MAP_EXTENSION=j, flag=1;
1167 /* Adjust up to a 256-byte boundary. */
1168 MEMORY_MAP_EXTENSION = (MEMORY_MAP_EXTENSION + 0xFF) & (~0xFF);
1170 if (strcmp(command,"TRANSCRIPT_FORMAT")==0)
1172 TRANSCRIPT_FORMAT=j, flag=1;
1173 if (TRANSCRIPT_FORMAT > 1 || TRANSCRIPT_FORMAT < 0)
1174 TRANSCRIPT_FORMAT = 1;
1176 if (strcmp(command,"WARN_UNUSED_ROUTINES")==0)
1178 WARN_UNUSED_ROUTINES=j, flag=1;
1179 if (WARN_UNUSED_ROUTINES > 2 || WARN_UNUSED_ROUTINES < 0)
1180 WARN_UNUSED_ROUTINES = 2;
1182 if (strcmp(command,"OMIT_UNUSED_ROUTINES")==0)
1184 OMIT_UNUSED_ROUTINES=j, flag=1;
1185 if (OMIT_UNUSED_ROUTINES > 1 || OMIT_UNUSED_ROUTINES < 0)
1186 OMIT_UNUSED_ROUTINES = 1;
1188 if (strcmp(command,"SERIAL")==0)
1190 if (j >= 0 && j <= 999999)
1192 sprintf(serial_code_buffer,"%06d",j);
1193 serial_code_given_in_program = TRUE;
1199 printf("No such memory setting as \"%s\"\n", command);
1201 printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n\
1202 It should be safe to omit it (putting nothing in its place).\n", command);
1206 printf("No such memory $ command as \"%s\"\n",command);
1209 extern void print_memory_usage(void)
1211 printf("Properties table used %d\n",
1212 properties_table_size);
1213 printf("Allocated a total of %ld bytes of memory\n",
1214 (long int) malloced_bytes);
1217 /* ========================================================================= */
1218 /* Data structure management routines */
1219 /* ------------------------------------------------------------------------- */
1221 extern void init_memory_vars(void)
1222 { malloced_bytes = 0;
1225 extern void memory_begin_pass(void) { }
1227 extern void memory_allocate_arrays(void) { }
1229 extern void memory_free_arrays(void) { }
1231 /* ========================================================================= */