From 6fb5823b1d2ad017eb0a9944a7a7732c68955ffb Mon Sep 17 00:00:00 2001 From: Ben Finney Date: Thu, 14 Apr 2016 22:30:33 +1000 Subject: [PATCH] Imported Upstream version 6.33 --- DebugFileFormat.txt | 385 +++++++ ReleaseNotes.html | 1214 ++++++++++++++++++++ arrays.c | 658 +++++++++++ asm.c | 3172 +++++++++++++++++++++++++++++++++++++++++++++++++++ bpatch.c | 511 +++++++++ chars.c | 1297 +++++++++++++++++++++ directs.c | 1139 ++++++++++++++++++ errors.c | 485 ++++++++ expressc.c | 2947 +++++++++++++++++++++++++++++++++++++++++++++++ expressp.c | 2035 +++++++++++++++++++++++++++++++++ files.c | 1760 ++++++++++++++++++++++++++++ header.h | 2808 +++++++++++++++++++++++++++++++++++++++++++++ inform.c | 1756 ++++++++++++++++++++++++++++ lexer.c | 1803 +++++++++++++++++++++++++++++ licence.txt | 238 ++++ linker.c | 1140 ++++++++++++++++++ memory.c | 1061 +++++++++++++++++ objects.c | 2256 ++++++++++++++++++++++++++++++++++++ readme.txt | 5 + states.c | 2703 +++++++++++++++++++++++++++++++++++++++++++ symbols.c | 1381 ++++++++++++++++++++++ syntax.c | 716 ++++++++++++ tables.c | 1922 +++++++++++++++++++++++++++++++ text.c | 2310 +++++++++++++++++++++++++++++++++++++ veneer.c | 2393 ++++++++++++++++++++++++++++++++++++++ verbs.c | 921 +++++++++++++++ 26 files changed, 39016 insertions(+) create mode 100644 DebugFileFormat.txt create mode 100644 ReleaseNotes.html create mode 100644 arrays.c create mode 100644 asm.c create mode 100644 bpatch.c create mode 100644 chars.c create mode 100644 directs.c create mode 100644 errors.c create mode 100644 expressc.c create mode 100644 expressp.c create mode 100644 files.c create mode 100644 header.h create mode 100644 inform.c create mode 100644 lexer.c create mode 100644 licence.txt create mode 100644 linker.c create mode 100644 memory.c create mode 100644 objects.c create mode 100644 readme.txt create mode 100644 states.c create mode 100644 symbols.c create mode 100644 syntax.c create mode 100644 tables.c create mode 100644 text.c create mode 100644 veneer.c create mode 100644 verbs.c diff --git a/DebugFileFormat.txt b/DebugFileFormat.txt new file mode 100644 index 0000000..8ed993c --- /dev/null +++ b/DebugFileFormat.txt @@ -0,0 +1,385 @@ +Format of Inform 6 Debugging Information Files + +Version 1.0 + +0: Introduction + +This is a specification of the Version 1 format for the debugging information +files emitted by the Inform 6 compiler. It replaces Version 0, which is +documented in Section 12.5 of the Inform Technical Manual. + +1: Overview + +Debugging information files are written in XML and encoded in UTF-8. They +therefore begin with the following declaration: + + + +Beyond the usual requirements for well-formed XML, the file adheres to the +conventions that all numbers are written in decimal, all strings are +case-sensitive, and all excerpts from binary files are Base64-encoded. + +2: The Top Level + +The root element is given by the tag with three attributes, +the version of the debug file format being used, the name of the program that +produced the file, and that program's version. For instance, + + + ... + + +The elements from Sections 3--8 may appear in the ellipses. + +3: Story File Prefix + +The story file prefix contains a Base64 encoding of the story file's first bytes +so that a debugging tool can easily check whether the story and the debug +information file are mismatched. For example, the prefix for a Glulx story +might appear as + + + R2x1bAADAQEACqEAAAwsAAAMLAAAAQAAAAAAPAAIo2Jc + 6B2XSW5mbwABAAA2LjMyMC4zOAABMTIxMDE1wQAAMA== + + +The story file prefix is mandatory, but its length is unspecified. Version 6.33 +of the Inform compiler records 64 bytes, which seems sufficient. + +4: Story File Sections + +Story file sections partition the story file according to how the data will be +used. For the Inform 6 compiler, this partitioning is the same as the one that +the `z' flag prints. + +A record for a story file section gives a name for that section, its beginning +address (inclusive), and its end address (exclusive): + + + abbreviations table +
64
+ 128 +
+ +The names currently in use include those from Section 12.5 of the Inform +Technical Manual: + + abbreviations table + header extension (Z-code only) + alphabets table (Z-code only) + Unicode table (Z-code only) + property defaults + object tree + common properties + class numbers + individual properties (Z-code only) + global variables + array space + grammar table + actions table + parsing routines (Z-code only) + adjectives table (Z-code only) + dictionary + code area + strings area + +plus one addition for Z-code: + + abbreviations + +two additions for Glulx: + + memory layout id + string decoding table + +and three additions for both targets: + + header + identifier names + zero padding + +Names may repeat; Glulx story files, for example, sometimes have two zero +padding sections. + +A compiler that does not wish to subdivide the story file should emit one +section for the entirety and give it the name + + story + +5: Source Files + +Source files are encoded as in the example below. Each file has a unique index, +which is used by other elements when referring to source code locations; these +indices count from zero. The file's path is recorded in two forms, first as it +was given to the compiler via a command-line argument or include directive but +without any path abbreviations like `>' (the form suitable for presentation to a +human) and second after resolution to an absolute path (the form suitable for +loading the file contents). All paths are written according to the conventions +of the host OS. The language is, at present, either "Inform 6" or "Inform 7". +More languages may added in the future. + + + example.inf + /home/user/directory/example.inf + Inform 6 + + +If the source file is known to appear in the story's Blorb, its chunk number +will also be recorded: + + + example.inf + /home/user/directory/example.inf + Inform 6 + 18 + + +6: Table Entries; Grammar Lines + +Table entries are data defined by particular parts of the source code, but +without any corresponding identifiers. The element notes the +entry's type, the address where it begins (inclusive), the address where it ends +(exclusive), and the defining source code location(s), if any: + + + grammar line +
1004
+ 1030 + ... +
+ +Version 6.33 of the Inform compiler only emits tags for grammar +lines; these data are all located in the grammar table section. + +7: Named Values; Constants, Attributes, Properties, Actions, Fake Actions, + Objects, Classes, Arrays, and Routines + +Records for named values store their identifier, their value, and the source +code location(s) of their definition, if any. For instance, + + + MAX_SCORE + 40 + ... + + +would represent a named constant. Attributes, properties, actions, fake +actions, objects, arrays, and routines are also names for numbers, and differ +only in their use; they are represented in the same format under the tags +, , , , , , and +. (Moreover, unlike Version 0 of the debug information format, fake +actions are not recorded as both fake actions and actions.) + +The records for constants include some extra entries for the system constants +tabulated in Section 12.2 of the Inform Technical Manual, even though these are +not created by Constant directives. Entries for #undefed constants are also +included, but necessarily without values. + +Some records for objects will represent class objects. In that case, they will +be given with the tag rather than and include an additional +child to indicate their class number: + + + lamp + 5 + 1560 + ... + + +Records for arrays also have extra children, which record their size, their +element size, and the intended semantics for their zeroth element: + + + route + 1500 + 20 + 4 + true + ... + + +And finally, records contain an
and a element, +along with any number of the and elements, +which are described in Sections 9 and 10. The address is provided because the +identifier's value may be packed. + +Sometimes what would otherwise be a named value is in fact anonymous; unnamed +objects, embedded routines, some replaced routines, veneer properties, and the +Infix attribute are all examples. In such a case, the subelement +will carry the XML attribute + + artificial + +to indicate that the compiler is providing a sensible name of its own, which +could be presented to a human, but is not actually an identifier. For instance: + + + lantern.time_left + 1820 + 80 + ... + ... + + +Artificial identifiers may contain characters, like the full stop in +``lantern.time_left'', that would not be legal in the source language. + +8: Global Variables + +Globals are similar to named values, except that they are not interpreted as a +fixed value, but rather have an address where their value can be found. Their +records therefore contain an
tag in place of the tag, as in: + + + darkness_witnessed +
1520
+ ... +
+ +9: Local Variables + +The format for local variables mimics the format for global variables, except +that a source code location is never included, and their memory locations are +not given by address. For Z-code, locals are specified by index: + + + parameter + 1 + + +whereas for Glulx they are specified by frame offset: + + + parameter + 4 + + +If a local variable identifier is only in scope for part of a routine, it's +scope will be encoded as a beginning instruction address (inclusive) and an +ending instruction address (exclusive): + + + rulebook + 0 + 1628 + 1678 + + +Identifiers with noncontiguous scopes are recorded as one +element per contiguous region. It is possible for the same identifier to map to +different variables, so long as the corresponding scopes are disjoint. + +10: Sequence Points + +Sequence points are stored as an instruction address and the corresponding +single location in the source code: + + +
1628
+ ... +
+ +The source code location will always be exactly one position with overlapping +endpoints. + +Sequence points are defined as in Section 12.4 of the Inform Technical Manual, +but with the further stipulation that labels do not influence their source code +locations, as they did in Version 0 of the debug information format. For +instance, in code like + + say__p = 1; ParaContent(); .L_Say59; .LSayX59; + t_0 = 0; + +the sequence points are to be placed like this: + + <*> say__p = 1; <*> ParaContent(); .L_Say59; .LSayX59; + <*> t_0 = 0; + +rather than like this: + + <*> say__p = 1; <*> ParaContent(); <*> .L_Say59; .LSayX59; + t_0 = 0; + +11: Source Code Locations + +Most source code locations take the following format, which describes their +file, the line and character number where they begin (inclusive), the line and +character number where they end (exclusive), and the file positions (in bytes) +corresponding to those endpoints: + + + 0 + 1024 + 4 + 44153 + 1025 + 1 + 44186 + + +Line numbers and character numbers begin at one, but file positions count from +zero. + +In the special case where the endpoints coincide, as happens with sequence +points, the end elements may be elided: + + + 0 + 1024 + 4 + 44153 + + +At the other extreme, sometimes definitions span several source files or appear +in two different languages. The former case is dealt with by including multiple +code location elements and indexing them to indicate order: + + + + + 0 + 1024 + 4 + 44153 + 1025 + 1 + 44186 + + + + + 1 + 1 + 0 + 0 + 3 + 1 + 59 + + +The latter case is also handled with multiple elements. Note that indexing is +only used to indicated order among locations in the same language. + + + + + 2 + 12 + 0 + 308 + 12 + 112 + 420 + + + + + 0 + 1024 + 4 + 44153 + 1025 + 1 + 44186 + diff --git a/ReleaseNotes.html b/ReleaseNotes.html new file mode 100644 index 0000000..5aa014c --- /dev/null +++ b/ReleaseNotes.html @@ -0,0 +1,1214 @@ + + + + +Inform Release Notes + + +

Inform Release Notes

+

Inform Compiler 6.33, 6.32, 6.31, 6.30
Inform Library 6/11

+ +

Introduction

+This is a maintenance release of the Inform system for creating adventure games, intended to address +issues that have arisen since the 6.21 compiler and 6/10 library files were released in 1999, and +subsequently since the 6.30 compiler was released in 2004. The focus is primarily on fixing problems, +but a small number of enhancements are included. +

+Although just about all known bugs are fixed, the approach to enhancing Inform is more conservative. +The selection of suggestions to implement has been governed by three factors: +

    +
  • avoidance of changes which might cause existing games to misbehave; +

  • minimisation of features which would require updates to the Inform Designer’s Manual; +

  • essential simplicity. With Graham Nelson’s permission, this release has been produced by a +volunteer taskforce, whose enthusiasm has been tempered by a certain lack of familiarity with the +internals of Inform. Thus, we have concentrated on ‘safe’ changes; the implementation of some +good ideas has been postponed until we are more confident that we know what we’re doing. +

+Having said that, this release does incorporate one major advance. It is based on Andrew Plotkin’s +bi-platform — Z-machine and Glulx — compiler and library files, which were in turn derived from the +the 6.21 compiler and 6/10 library. The result is that the two Virtual Machine (VM) strands have +merged into a single compiler and library which, although continuing by default to produce Z-code, +can alternatively generate code for the Glulx VM if you supply the -G compiler switch. There’s more +on this topic in Support for Glulx. +Before that, though, we’ll summarise the changes to the compiler and to the library files. +

+If you’ve translated the 6/10 library into another language, you may find +Information for translators helpful. + +

Acknowledgements

+Far too many people contributed towards this release — reporting and resolving bugs, making helpful +suggestions, providing support and facilities, testing, and so on — for their names to be individually +listed. So instead, this is a general thank-you to everybody who has made this release happen, and +specific ones to Graham for permitting it in the first place, and to Andrew for his pioneering work on +Glulx. +There are, naturally, sure to be imperfections in the release, and these should be reported through the +Inform bug tracker. + +

Compiler 6.33

+These are the changes delivered in version 6.33 of the Inform compiler. +

Features added

+
    +
  • Unused routines can now be detected and omitted from the compiled game, through two +new settings. If $WARN_UNUSED_ROUTINES is set to 1, unused routines are reported during +compilation. If $OMIT_UNUSED_ROUTINES is set to 1, unused routines (other than those in the +Inform library) are omitted; if it is set to 2, unused routines anywhere, even in the library, are +omitted. +

  • A new command line switch -Cu can be used to specify that the source file +character set is UTF-8, allowing source files to contain Unicode characters beyond those defined in +ISO 8859-1 to 8859-9. +

  • A new #Undef directive will cause a previously defined constant to become undefined. +

  • #Ifdef directives are now allowed within an object declaration. +

  • Action statements can have an optional extra ‘actor’ argument, allowing action +statements of the form +

    +  <Take pie, orc>; ! simple form
    +  <<Look, floyd>>; ! auto-returning form
    +
    +Note that this also requires a library change to be useful. +
  • A previously declared routine can be renamed with a new form of the Replace directive. +For example, if a source file contained +

    +  Replace Banner OriginalBanner;
    +
    +It could then (after including the library) contain this definion of a function: +
    +  [ Banner;
    +    OriginalBanner();
    +    print "Extra version info!^";
    +  ];
    +
    +The library's banner code would then be in OriginalBanner(). +
  • The previously deprecated Dictionary directive can now have the following forms: +

    +  Dictionary 'word';
    +  Dictionary 'word' val1;
    +  Dictionary 'word' val1 val3;
    +
    +The first of these forms just adds the word to the dictionary, with all flags set to zero, if it is not +already in the dictionary. The second form also sets the dict_par1 flag to the given value, or +bitwise-or’s the value in if the word already exists. The third form also sets the dict_par3 +in the same way as for dict_par1. +

    +The values can be numeric literals or constants. They can be 0-255 for Z-code, or 0-65535 for Glulx. +

  • The “font on” and “font off” statements now call the +@set_font opcode for Z-code V5 and higher. +

  • The Glulx version of the Unsigned__Compare() veneer routine has been changed to a +more efficient implementation, using Glulx’s unsigned comparison opcodes. +

  • The debugging output file, generated when the -k is used, has been changed to a +new, XML-based format. +

  • Two new Z-code settings have been added to support the extra words in the header extension table +specified in section 11.1.7 of the Z-Machine Standards Document version 1.1. $ZCODE_HEADER_EXT_WORDS +specifies how many extra words to add (so for all three words defined in the 1.1 standard, this would +be set to 3). $ZCODE_HEADER_FLAGS_3 specifies the value to put in the first of these three words, +which is the "flags3" field. +

  • A new Glulx setting $GLULX_OBJECT_EXT_BYTES has been added, which specifies an +amount of additional space to add to each object record. The default is 0. +

+

Bugs fixed

+Items of the form [Mantis 510] quote the bug’s reference number in Inform 7’s Mantis bug tracker. +
    +
  • Function calls of the form f(g)(h) (that is, where f() returns the address of a +function that is called with h as its argument) are now compiled correctly: previously, such calls were +compiled as if the code was f(g(h)). [Mantis 510] +

  • The bounds checking related to internal arrays that are are sized from various compiler settings +has been improved, so that it should not be possible to crash the compiler if these settings are too small. +In addition, a start has been made on allowing the compiler to grow its internal buffers, rather than relying +on these settings to specify sufficient buffer sizes. [Mantis 601,603,610,614,758,842,844] +

  • The error message shown when too many global variables are declared now tells the user to +increase the $MAX_GLOBAL_VARIABLES setting. [Mantis 611] +

  • The setting $MAX_CLASS_TABLE_SIZE, which was not used anywhere, has been removed. [Mantis 612] +

  • The compiler no longer crashes if run with the -k switch and passed a source file +containing no routines at all. [Mantis 843] +

  • Floating-point constants in “#Ifdef TARGET_GLULX;” sections do not now cause an error +when compiling to Z-code. [Mantis 927] +

  • The error message produced if an action routine is not found for an action name has been improved to include +the action name, and a sensible line number. [Mantis 1014] +

  • The compiler is now better at not producing spurious additional errors after it has encountered a directive +that contains an error. [Mantis 1018,1035] +

  • The compiler no longer crashes when reporting errors after line 65,535 in long Inform 6 source files. [Mantis 1023] +

  • The compiler now reports a meaningful text compression rate when outputting statistics for Glulx with the +-Gs switches. [Mantis 1026] +

  • An error is now reported if a source file ends with an unterminated object definition. [Mantis 1036] +

  • The three argument form of the read statement no longer assumes that the routine passed as the +third argument to the statement will not change the global variable that the statement uses internally. [Mantis 1084] +

  • The 'Abbreviate' statement now works with words containing non-English characters. [Mantis 1130] +

  • Attempting to use @pop opcode for V5 or higher no longer results in a crash. [Mantis 1172] +

  • The Glulx setting $NUM_ATTR_BYTES, which determines the number of bytes in an object set aside +for attribute flags, now works correctly. Note that this has required a change to the veneer routines that conflicts +with the definition of FUNC_2_CP__Tab() in the ‘Accelerated Functions’ section of the Glulx 3.1.2 +specification. If you change this setting, you should take great care if you also use the Glulx accelerated functions +feature (as Inform 7 does by default). [Mantis 1197] +

  • The syntax of declaring a function so that it prints its name and arguments: +

    +  [ Whatever * x y;
    +    ! Whatever function logic ...
    +  ];
    +
    +is now supported for Glulx. [Mantis 1214] +
  • The statistics produced by the -s compiler switch are now correct for Glulx. [Mantis 1267] +

+ +

Compiler 6.32

+These are the changes delivered in version 6.32 of the Inform compiler. +

Features added

+Items of the form [C63104] quote the feature’s reference number in the ‘Compiler’ section of the Inform +Patch List. +
    +
  • The Z-machine opcodes for pushing and pulling values from the stack are @push and @pull, used like this: +

    +  @push x;
    +  @pull x;
    +
    +However for Glulx the opcode syntax is different: instead the @copy opcode is used: +
    +  @copy x sp;
    +  @copy sp x;
    +
    +The compiler now supports the @push and @pull syntax under Glulx as an alias for @copy, allowing +the same code to be used for both the Z-machine and Glulx. [C63104] +
  • Custom Glulx opcodes (such as opcodes that post-date the compiler) can now be specified with the custom opcode syntax. +The format of this syntax is +

      @"FlagsCount:Code"
    +
    +Flags (which are optional) can include “S” for store, “SS” for two stores, “B” for branch format, +or “R” if execution never continues after the opcode. Count is the number of arguments (currently limited to 0-9), +and Code is a decimal integer representing the opcode number.

    +For example, @"S3:123" is the syntax for a three-argument opcode (load, load, store) whose opcode number in decimal +is 123, and @"2:234" is the syntax for a two-argument opcode (load, load) whose number is 234. [C63107] +

  • When compiling to Glulx, the Glulx format version number of the resulting story file is usually determined by which Glulx opcodes +are used in the source code. This version number can now be over-ridden by providing a -v command line argument after the +-G switch to select Glulx mode. For example, the arguments +-G -v3.1.0 set the Glulx version number to “3.1.0”. [C63108] +

  • The Unicode related opcode added to the Glulx 3.0.0 specification, @streamunichar, is now supported. +

  • When compiling to Glulx, characters outside of the ISO 8859-1 range can now be used in strings. The maximum number of such +characters allowed is determined by a new memory setting, $MAX_UNICODE_CHARS. +

  • When compiling to Glulx, the syntax +

      print (char) value;
    +now works for values greater than 255, printing the appropriate Unicode character. (For such values @streamunichar is used; +for those less than or equal to 255, @streamchar is used as before.) +
  • The memory heap related opcodes added to the Glulx 3.1.0 specification (that is, +@mzero, @mcopy, @malloc and @mfree) are now supported. +

  • The acceleration related opcodes added to the Glulx 3.1.1 specification (that is, +@accelfunc and @accelparam) are now supported. There is also a new syntax to get the address of a global variable +var, with the expression “#g$var”: this is provided so that such addresses can be provided +to the @accelparam opcode. +

  • The floating point related opcodes added to the Glulx 3.1.2 specification (that is, +@numtof, @ftonumz, @ftonumn, @ceil, @floor, @fadd, @fsub, @fmul, +@fdiv, @fmod, @sqrt, @exp, @log, @pow, @sin, @cos, @tan, +@asin, @acos, @atan, @atan2, @jfeq, @jfne, @jflt, @jfle, +@jfgt, @jfge, @jisnan and @jisinf) are now supported. +

  • Floating point constants can be used in the Inform source code. Thes constants are expressed in the form “$+1.0e+1”: +that is, a dollar sign, followed by a plus or minus sign, followed by a floating point number, and then optionally a positive or negative +integer exponent. Inform does not attempt anything other than converting these constants to their 32-bit integer representation: there is +no constant folding as there is with integers, so the expression “$+1.0 + $+1.0” is not meaningful, and does not +produce the floating point value “2.0”.

    +As an example of the use of these constants, the following adds together 100 and 123.45: +

      @fadd $+100 $+1.2345e+2 result;
    +The compiler also defines the constants “FLOAT_INFINITY”, “FLOAT_NINFINITY” +and “FLOAT_NAN” for positive infinity, negative infinity and “not a number”, respectively. +
  • Glulx has a simple memory extension feature, where the game’s header declares the memory space to be larger than the game file. +The extra space is filled out with zeroes when the game starts. This is now supported by a new option $MEMORY_MAP_EXTENSION. +This defaults to 0: if it is redefined to a larger value then the size of the game’s memory space is extended by that amount. +

  • The number of verbs allowed by the compiler when compiling to Glulx is no longer limited to fewer than 256. As part of producing +the game file, the compiler creates a dictionary table that contains verb numbers. However, despite in Glulx there being space for a verb +number between 0 and 65535 in this table, only one byte of it (that is, values between 0 and 255) was used. This has been fixed.

    +However, this also requires library changes to be useful, as the library makes use of this dictionary table. The library used by Inform 7 +has been adjusted to take advantage of this, but the Inform 6/11 library has not. +

  • The dictionary of Glulx games can now contain characters outside of ISO 8859-1. There is a new +memory setting, $DICT_CHAR_SIZE: by default this is 1, but setting it to 4 causes the compiler to create a dictionary containing +32-bit Unicode characters.

    +However, this also requires library changes to be useful, as the library makes use of this dictionary table. +

+

Bugs fixed

+Items of the form [C63102] quote the bug’s reference number in the ‘Compiler’ section of the Inform +Patch List. +
    +
  • Strict mode is no longer enabled by default when generating V3 and V4 Z-code files, as strict mode +makes use of opcodes that are V5+ only. [C63007] +

  • The base unit of Inform’s internal memory allocation for various structures (for example the buffer used +to hold the generated code) is no longer fixed: it is now controlled by a setting $ALLOC_CHUNK_SIZE. +This allows, for example, the maximum Glulx code size to be greater than 640Kb. [C63102] +

  • When compiling to Glulx, the stack size of the resulting story file is no longer fixed at 4096: it can be changed +by the setting $MAX_STACK_SIZE. [C63108] +

  • The compiler could crash if the size of the grammar table exceeded the size of the Z-machine readable memory: this +is now fixed. [C63110] +

  • Creating a Z-code game file with precisely 64Kb of readable memory produced an invalid file. This is now prevented, +so that the largest readable memory size is 64Kb minus 2 bytes. [C63112] +

  • Previously, under Glulx the print_to_array() function could be called with either two arguments, specifying both +the array to print to and its length, or just one argument, the later matching what is allowed when compiling to Z-code. This +one argument form has now been withdrawn under Glulx as a security hole, and a source to problems with writing beyond the end +of the array: now the array length must be specified. (See also Features available only in Glulx.) +

  • Veneer routines are no longer excluded from Inform’s assembly output (which is accessed with the -a +command line switch). +

  • For Linux and other Unix variants the default memory settings have been increased, which should remove the need +to change the compiler settings when building large projects on these platforms. +

  • For Mac OS X, the maximum length of a file path is now 8192 characters, which should prevent any further problems with +long paths on this platform. +

+ +

Compiler 6.31

+These are the changes delivered in version 6.31 of the Inform compiler. +

Bugs fixed

+Items of the form [C63016] quote the bug’s reference number in the ‘Compiler’ section of the Inform +Patch List. +
    +
  • When individual properties are read (successfully), there is no longer a futile attempt to look up +the property index in the common property table. [C63016] +

  • Complex if statements, of the form if (x == 1 or 3 or 5 ... or 99), now don’t crash the +compiler. [C63013] +

  • The compiler is better at handling lengthy path names. [C63009] +

  • Backpatching and other strange errors no longer occur if the code size of a V8 game exceeds +256K. [C63005] +

  • An Object or Class definition with more than 32 common property values now doesn’t cause an +internal compiler error. [C63001] +

+ +

Compiler 6.30

+These are the changes delivered in version 6.30 of the Inform compiler. +

Features added

+
    +
  • The compiler automatically defines a WORDSIZE constant, whose value is 2 when compiling for the +Z-machine, and 4 when the target is Glulx. The constant specifies the number of bytes in a VM +word, and we recommend that you use it in the small number of circumstances where this value is +significant. The compiler also defines a constant TARGET_GLULX if you supply the -G switch, or +TARGET_ZCODE otherwise; in both cases the constant value is 0. For more information on the use of +these constants, see Support for Glulx. +

  • The Switches directive, which enables certain compiler switches to be set from within the source +file rather than on the compiler command line, has been superseded by a more powerful +mechanism. The special comment characters “!%”, occurring on the very first line or lines of the +source file, enable you to specify Inform Command Language (ICL) commands to control the +compilation. For example: +

    +  !% -E1G                                    ! Glulx, 'Microsoft' errors 
    +  !% -~S                                     ! disable Strict mode 
    +  !% +include_path=./test,./,../lib/contrib  ! look in 'test' library 
    +  !% $MAX_STATIC_MEMORY=20000 
    +  Constant STORY "RUINS"; 
    +  ...
    +
    +ICL is described in §39 of the Inform Designer’s Manual. In brief: each line specifies a single +command, starting with “-” to define one or more switches, “+” to define a path variable, or “$” to +define a memory setting. Comments are introduced by “$”. The ICL command “compile” is not +permitted at the head of a source file. +
  • Two new ICL memory settings are available; both of these could previously be changed only by +rebuilding the compiler. $MAX_SOURCE_FILES has a default of 256, and $MAX_INCLUSION_DEPTH has a +default value of 5. +

  • A new directive, similar to Array ... string and Array ... table, is provided: +

    +  Array array buffer N; 
    +  Array array buffer expr1 expr2 ... exprN; 
    +  Array array buffer "string";
    +
    +This creates a hybrid array of the form used by string.print_to_array() and the new library +routine PrintToBuffer(), in which the first word array-->0 +contains N and the following N bytes +array->WORDSIZE, array->(WORDSIZE+1) ... array->(WORDSIZE+N-1) contain the specified +expression values or string characters. +
  • A new (A) print rule — similar to the existing (The) — prints an object’s indirect article with its first +letter capitalised. The printed article is “A” or “An” by default, or else taken from the object’s +article property. +

  • The minimum size of the Z-code header extension table can be set using the command line switch +-Wn. For example, -W6 makes the table at least six words long. +

  • Source code in character sets other than ISO 8859-1 to 8859-9 is now supported, provided that the +character set can be mapped onto one of the ISO 8859 sets.

    +A mapping file is used to define how the source code is to be processed. This file consists of a +directive indicating the ISO 8859 set to be mapped to, followed by 256 numbers giving the +mapping. As an example, under Microsoft Windows, Russian text is encoded with the character +set defined as Microsoft code page 1251. The following text defines a mapping to the ISO 8859-5 +set: +

    +  ! Windows Cyrillic (code page 1251) to ISO 8859-5
    +  C5
    +    0, 63, 63, 63, 63, 63, 63, 63, 63, 32, 10, 63, 10, 10, 63, 63 
    +   63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 
    +   32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 
    +   48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 
    +   64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 
    +   80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 
    +   96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111 
    +  112,113,114,115,116,117,118,119,120,121,122,123,124,125,126, 63 
    +  162,163, 44,243, 34, 46, 63, 63, 63, 63,169, 60,170,172,171,175 
    +  242, 39, 39, 34, 34, 46, 45, 45,152, 84,249, 62,250,252,251,255 
    +   32,174,254,168, 36, 63,124,253,161, 67,164, 60, 63, 45, 82,167 
    +   63, 63,166,246, 63, 63, 63, 46,241,240,244, 62,248,165,245,247 
    +  176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191 
    +  192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207 
    +  208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223 
    +  224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239 
    +
    +Lines starting with “!” are treated as comments. The next line, beginning with “C”, defines the +ISO set to map to in the same way as the -Cn command line switch.

    +To use the mapping, Inform treats each character in the source file as a number between 0 and +255, and uses that number as an index into the mapping table. For example, suppose that the +character read in from a Russian Windows text file is the Cyrillic small letter “ya”.This character +is represented in the Russian Windows character set by the number 255. Inform takes the 255th entry in +the mapping, which is 239. Therefore the character is regarded as being 239 in ISO 8859-5.

    +The name of the mapping file is specified by a new compiler path variable +charset_map. If the +above mapping is held in a text file win1251.map, a Russian game could be compiled with a +command line of the form: +

    +  inform +charset_map=win1251.map +language_name=Russian mygame.inf
    +
    +
  • The @check_unicode and @print_unicode opcodes, introduced in the Z-Machine Standards +Document version 1.0, can now be called by name rather than by using the clumsier generic +syntax @"EXT:11S" and @"EXT:12S". For example: +

    +  @print_unicode $0401; ! Cyrillic capital letter IO
    +
    +
  • Strict mode (which compiles run-time integrity checks into your game) has been decoupled from +Debug mode (which defines debugging verbs like TRACE and SHOWOBJ). This means that it’s +no longer necessary to turn off Strict checking (in order to disable the Debug verbs) before +releasing a game, though of course you can do so if you wish to save space and increase +performance. By default, Strict mode is enabled (turn it off with -~S) and Debug mode is +disabled (turn it on with -D). +

  • The compiler now issues a warning if you use array->n on an array of words, or array-->n on an +array of bytes. Use the new Array ... buffer directive to create a hybrid array which can be +accessed without these warnings. +

  • The compiler also issues a warning if you refer to an unqualified property name within a routine, +for example by typing (number==0) when you intended (self.number==0). +

  • Another new warning appears if you include something other than a dictionary word in an +object’s name property. This is most commonly triggered if you try to add a single-letter word by +typing name 'g' 'string' when you intended name 'g//' 'string'. +

+

Bugs fixed

+Items of the form [C62127] quote the bug’s reference number in the ‘Compiler’ section of the Inform +Patch List. +
    +
  • A problem with multiple assignment involving pointer arithmetic in Strict mode giving the wrong +answer has been fixed. [C62127] +

  • After using Extend only to separate off an element of an existing Verb definition, new synonyms +for the separated verb now work correctly; previously they were applied to the residue of the +original definition. [C62126] +

  • Strict mode now tests for the use of @put_prop or @get_prop opcodes when a common property is +longer than two bytes — the Z-Machine Standards Document says that this is illegal, and that the +result is unspecified. The error message is of the form “[** Programming error: obj (object +number N) has a property prop, but it is longer than 2 bytes so you cannot use "." to read **]”. This +means that you have used the obj.prop construct in the situation where prop is a common +property containing two or more values; typically prop is being explicitly used as an array, or it’s +an additive property and both obj and its parent class have defined values for it. The problem does +not occur if prop is an individual property. [C62125] +

  • Handling of European quotes is (finally) correct: the “«” symbol is produced by any of @<<, @@163 +and @{00AB}, while any of @>>, @@162 and @{00BB} produce the matching “»”. Note, however, that +this problem originated in an error in the previous version of the Z-Machine Standards Document, +and therefore older interpreters written to that specification, or more recent ones adjusted to work +with the incorrect fix introduced at Inform 6.12, may still not give the correct results. [C62124] +

  • The “no such constant” compilation error message now quotes the number of the appropriate +source line. [C62123] +

  • The metaclass() and ZRoutine() routines no longer report large unsigned values — above the +game’s Static memory area — as of type String. More usefully, the constant NULL (-1) is not +reported as a String. [C62122] +

  • Complex expressions combining a routine call and the ofclass and or operators no longer +generate incorrect code. [C62121] +

  • Negative constants in assembly operations — for example, @set_colour 4 (-1); — no longer cause +the compiler to report an unexpected expression. [C62119] +

  • Various problems with handling ISO 8859 characters in the range 128-255, and also in the use of +@@ escape sequences, have been resolved. [C62117, C62115, C6211, C62003] +

  • An Abbreviate directive containing a substring of “<unknown attribute>” may crash the +compiler; hopefully, no more. [C62116] +

  • The 320Kb size limit placed by Inform on v6 and v7 games has been raised to 512Kb. [C62114] +

  • Following a Zcharacter directive replacing the entire alphabet table, dictionary entries are no +longer corrupted. [C62113] +

  • The compiler now generates conditional branches spanning up to 63 bytes, lifting the previous +unnecessary limit of a 31-byte span and leading to slightly shorter code. [C62112] +

  • Putting an object in itself doesn’t now loop indefinitely. [C62110] +

  • Various problems with the @store, @inc_chk, @dec_chk, @not and @je opcodes have been resolved. +[C62109, C62108, C62105] +

  • Problems with nested conditional compilation directives #Ifndef...#Ifnot...#Endif have been +resolved. [C62107] +

  • A long dictionary word — such as 'elephants//p' — now correctly sets the plural bit. [C62103] +

  • Problems with constant folding — that is, having the expression evaluated at compile-time — partly +addressed in the previous bi-platform compiler, have been fixed. [C62102] +

  • When compiling for Glulx, the compiler uses the @callf, @callfi, @callfii or @callfiii opcodes +where applicable for generated calls instead of always pushing arguments onto the stack and +using @call. +

  • The presence of a Switches G; directive no longer causes the compiler to crash. +

  • An unexpected limit of 1024 labels per routine in the Z-machine assembly language generated by +the compiler has been raised to 32768. The most likely way to encounter this limit is by creating a +switch statement with an extremely large number of cases. +

  • A problem with the read statement generating the wrong opcode in a version 4 game has been +corrected. +

  • A dynamic class declaration such as Class Pebble(NUM_PEBBLES) ... ; no longer creates a +mysteriously large number of instances if NUM_PEBBLES isn’t defined. +

+ +

Library 6/11

+These are the changes delivered in version 6/11 of the Inform library. +

Features added

+
    +
  • The library automatically defines four constants: LIBRARY_PARSER at the end of Parser.h, +LIBRARY_VERBLIB at the end of VerbLib.h, LIBRARY_GRAMMAR at the end of Grammar.h, and +LIBRARY_ENGLISH at the end of English.h. Contributed library extensions can use these constants to +check that they have been Included in the correct location. A fifth constant LIBRARY_VERSION, +currently defined as the number 611, can be checked by extensions which require this particular +version of the library. +

  • The word “wall” has been removed from the CompassDirection objects defined in English.h, +whose names are now simply “north”, “south”, etc. +

  • The verbs LOOK [TO THE] NORTH, LOOK DOWN, LOOK OUT[SIDE] etc — but not +LOOK IN[SIDE], which is already available — have been added. By default, the response is of the +form “You see nothing unexpected ...”, but you can change this for individual directions in +individual rooms by providing a compass_look property: +

    +  Room   study "Your study" 
    +    with description "There is a doorway to the east of this austere room.", 
    +         compass_look [ obj; 
    +             if (obj == e_obj) "You see a doorway."; 
    +             if (obj == n_obj or s_obj or w_obj) "You see the wall."; 
    +         ], 
    +         e_to hallway; 
    +
    +This enhancement uses the mechanism described in +this topic in the Inform 6 FAQ +(except that the compass_look property was previously named compasslook), and means that you +no longer need to make the library changes described there. +
  • The verbs ASK npc TO command and TELL npc TO command +— both synomymous with npc,command — +are provided. The new grammar is: +

    +  Verb 'ask' 
    +      ...
    +      * creature 'to' topic -> AskTo 
    +      ...
    +
    +in which the creature token matches the npc and the topic token represents +the command. AskTo isn’t an action in the usual sense: it’s trapped by the parser and +converted to the original npc,command format. The npc can intercept the +command by providing an orders property in the usual way — +see §18 of the Inform Designer’s Manual.

    +This enhancement means that you may no longer require Irene Callaci’s AskTellOrder.h library +extension. +

  • The verbs RECORDING [ON|OFF] and REPLAY are now always available, irrespective of the +DEBUG state. This may cause compilation errors if you have already defined these verbs +yourself. +

  • The verbs PRY, PRISE, PRIZE and LEVER have been added. This may cause compilation errors +if you have already defined these verbs yourself. +

  • The parser treats input lines beginning with “*” as a comment, without attempting any further +parsing. The character used to introduce comments can be changed by defining +COMMENT_CHARACTER before you Include Parser. For example: +

      Constant COMMENT_CHARACTER '!';
    +Since comments are used primarily when capturing a transcript — either of a complete game +(SCRIPT ON) or of input commands only (RECORDING ON) — the parser responds “[Comment +recorded]” or “[Comment NOT recorded]” as appropriate. +
  • The selfobj object now includes an empty add_to_scope property, which you can over-ride with +your own routine, typically to equip the player with body parts. For a single object: +

      selfobj.add_to_scope = nose;
    +or for multiple objects: +
    +  [ IncludeBodyParts; PlaceInScope(nose); PlaceInScope(hands); ];
    +  selfobj.add_to_scope = IncludeBodyParts;
    +
    +
  • The task-based scoring system (§22 of the Inform Designer’s Manual) uses a byte array, which +precludes the awarding of large or negative scores. To get round this, you can Replace the +TaskScore() library routine as follows, and then define task_scores as a word array: +

    +  Replace TaskScore;
    +  Array task_scores --> 100 200 300 400 (-50) 600;
    +  [ TaskScore i; return task_scores-->i; ];
    +
    +
  • The scoring system is completely disabled if you define a constant NO_SCORE near the start of your +game. +

      Constant NO_SCORE;
    +
  • A new before_implicit property is available; at the moment this is used only by the parser, when +it is about to perform an implicit TAKE (for example, EAT APPLE when you’re not holding the +apple). You can give this property to an object if you wish to control the parser’s behaviour. The +property’s value should be a constant or a routine which returns: 0 to report “(first taking the...)” +and then attempt to do so (this is what currently happens); 1 to attempt the TAKE without first +issuing the message, 2 to proceed with the requested action without attempting the TAKE, or 3 to +object that “You aren’t holding that!”. The object can test action_to_be to determine which action +has triggered the TAKE: +

    +  before_implicit [;
    +    Take: if (action_to_be == ##Eat) return 2;
    +  ],
    +
    +
  • A new system variable sys_statusline_flag is set to 1 initially if you have used the +statusline time; directive in your program to show a clock, and to 0 otherwise. It can be +changed by the program. +

  • An object’s invent property — if it has one — is now invoked both when displaying the player’s +inventory and when including the object in a room description. invent is invoked in the usual +way (with inventory_stage first set to 1, and then set to 2) both when mentioning the object in a +room description, and when listing it in the player’s inventory. By default you’ll get the same +output each time. If you need to distinguish between the two occasions, you can test +(c_style&PARTINV_BIT) — true during a room description — or (c_style&FULLINV_BIT) — true +during an inventory. Here’s an example: +

    +  Object  -> "sack" 
    +    with  name 'sack', 
    +          invent [; 
    +              ! When listing objects in the player's inventory 
    +              if (c_style&FULLINV_BIT) rfalse; 
    +
    +              ! When listing objects at the end of a room description 
    +              if (inventory_stage == 1) switch (children(self)) { 
    +                  0: print "an empty sack"; 
    +                  1: print "a sack containing ", (a) child(self); 
    +                  default: print "an assortment of objects in a sack"; 
    +                  } 
    +              rtrue; 
    +              ], 
    +    has   container open; 
    +
    +This enhancement uses the mechanism described in +this topic in the Inform 6 FAQ +and means that you no longer need to Include WriteList. +
  • The turns counter is now initialised to 0, not 1. You can change this if you define a constant +START_MOVE near the start of your game. +

      Constant START_MOVE 1;
    +
  • A new LibraryExtensions object is defined, whose function is to act as a parent to initialisation +objects created by library extensions. These objects may provide ext_initialise and/or +ext_messages property routines, whose role is to help integrate the extension into a game. This is +best explained by example.

    +Consider the SmartCantGo.h extension, which replaces “You can’t go that way” messages by the +more informative “You can go only north, south and east”, and can be integrated into a game by +adding a ChangeDefault(cant_go, SmartCantGo) statement to your Initialise() routine. Instead +of requiring the author to make this addition, the extension could now cause it to happen +automatically by defining an initialisation object as a child of LibraryExtensions, like this: +

    +  Object  "(SmartCantGo)" LibraryExtensions 
    +    with  ext_initialise [; ChangeDefault(cant_go, SmartCantGo); ];
    +
    +Just before calling the game’s Initialise() routine, the library loops through the children — if any +— of LibraryExtensions, and executes those ext_initialise properties that it finds there. The +property routines can perform any appropriate setup processing that would otherwise have to be +inserted into the Initialise() routine itself; for example, starting a daemon running.

    +A similar process takes place when displaying library messages. The library first checks whether +the author has provided a LibraryMessages object to intercept the message which it is about to +display. If not, it now loops through the children of LibraryExtensions, and executes +ext_messages properties that it finds there. If none of those routines returns true to signal that the +message has been dealt with, the standard library text is then printed in the usual way. For +example, here’s how an extension might automatically intercept Inventory messages (unless the +game has already handled them via LibraryMessages): +

    +  Object  "(someInventoryExtension)" LibraryExtensions 
    +    with  ext_messages [;
    +              Inv: switch(lm_n) {
    +                1: "You are empty-handed.";
    +                2: "Your possessions include";
    +              } 
    +          ];
    +
    +Note that this is an experimental feature, and may be modified or extended in the light of +experience. +
+

Library routines

+Several new library routines are provided to harmonise commonly-encountered differences between +the Z-machine and Glulx VMs (see also Library routines available only in Glulx). +

KeyCharPrimitive() +

waits for a single key, and returns the character from 1-255 (or, for Glulx, one of the Glk special +key codes.). For Glulx only, an extended form is available — +see Library routines available only in Glulx.
+

KeyDelay(time) +

waits time tenths of a second for a single key. If no key is pressed within that period it returns +zero; otherwise it returns the character from 1-255.
+

ClearScreen()
+ClearScreen(selector)
+

ClearScreen() clears both the status line and the main window. The cursor moves to the top of the +screen. The routine should be followed by a call to MoveCursor() or MainWindow(). Alternatively, +using ClearScreen(selector): if selector is 0, both are cleared; if selector is 1 only the +status line is cleared; if selector is 2 only the main window is cleared.
+

MoveCursor()
+MoveCursor(line, column)
+

MoveCursor() selects the status line for output. MoveCursor(line, column) selects the status line +for output and moves the cursor to the given line and column within the status area, where line 1 +is the top line and column 1 is the far left. (This is necessary because the Glk convention is to +number both lines and columns from 0 rather than 1.)
+

MainWindow() +

selects the main (buffered) text window for output.
+

StatusLineHeight(lines) +

sets the height of the status line in lines. The standard DrawStatusLine() calls this every turn, +which isn’t a bad thing, since StatusLineHeight() is smart. If you replace DrawStatusLine(), +maintain this convention. (The library menu routines fiddle with the status line, and it’s up to +DrawStatusLine() to reset it after the menus are over.)

+A new library variable gg_statuswin_cursize holds the current setting (in both VMs).

+

ScreenWidth() +

returns the number of characters that can be printed in a monospaced font between the left and +right borders of the currently selected window. For Glulx only, the extended form +ScreenWidth(win) works on a specified window id; note that the results are unreliable if the +normal style for that window uses a proportional font.
+

ScreenHeight() +

returns the height in lines of the main window.
+

SetColour(fg, bg)
+SetColour(fg, bg, selector)
+

SetColour(fg, bg) sets the current foreground and background text colours, using the same codes +as the @set_colour opcode in the Z-machine (1=default, 2=black, 3=red, 4=green etc.). Using +SetColour(fg, bg, selector), colours can be set separately in each window: if selector is 0, +both are set (the top window will have inverted colours for the Z-machine); if selector is 1 only +the status line is affected; if selector is 2 only the main window is affected.

+All colour functions are effective only if the library variable clr_on is set to non-zero.

+The advantage over @set_colour is that when the player restores a saved game or types UNDO, +the colours will be correct for that state of the game.

+For Glulx, the routine produces an appropriate effect if style hints are enabled by the interpreter; it +also clears the screen. For the Z-machine, a separate call to ClearScreen() is required.

+These associated constants are now provided for use with SetColour(); the final three are useful +also with ClearScreen(): +

+  Constant CLR_DEFAULT 1; 
+  Constant CLR_BLACK 2; 
+  Constant CLR_RED 3; 
+  Constant CLR_GREEN 4; 
+  Constant CLR_YELLOW 5; 
+  Constant CLR_BLUE 6; 
+  Constant CLR_MAGENTA 7; 
+  Constant CLR_CYAN 8; 
+  Constant CLR_WHITE 9; 
+  Constant CLR_PURPLE 7; 
+  Constant CLR_AZURE 8; 
+  Constant WIN_ALL 0; 
+  Constant WIN_STATUS 1; 
+  Constant WIN_MAIN 2;
+
+

DecimalNumber(num) +

prints num as a decimal number (it is in fact identical to print num;). It may be useful in +conjunction with...
+

PrintToBuffer(array, arraylen, string)
+PrintToBuffer(array, arraylen, obj)
+PrintToBuffer(array, arraylen, obj, prop)
+PrintToBuffer(array, arraylen, routine, arg1, arg2)
+

prints its arguments — a string, an object’s name, the value of an object’s property, or a routine +with up to two arguments — to the buffer array. The number of characters written to the buffer is a +word at array-->0 (and is the value returned by the routine); the actual characters start at +array->WORDSIZE. The maximum number of characters is specified in arraylen; for the +Z-machine, an overrun caused by printing more than this value will produce an error message that +you have corrupted the contents of memory beyond the array (for Glulx, the output is +automatically truncated at the specified arraylen).

+For Glulx, see also PrintAnyToArray() in Library routines available only in Glulx, +which has slightly extended capabilities, and which returns the number of characters written +rather than writing them at array-->0.

+

Length(string)
+Length(obj, prop)
+

returns the number of characters in the string. Note that this prints to one of the parser arrays, and +therefore it is your responsibility to ensure that the length cannot be greater that 160 +characters.
+

UpperCase(char)
+LowerCase(char)
+

return char in upper or lower case (if it was alphabetic), or unchanged (otherwise). Changes +affecting A-Z and a-z are always reliable; changes to accented characters will not work if you +have supplied a compiler switch -C2 through -C9, or used a Zcharacter directive to adjust the +standard ZSCII character set.
+

PrintCapitalised(obj, prop, flag, nocaps) +

is based upon PrintOrRun(obj, prop, flag). PrintOrRun() tests +obj.prop, and either runs it (if a +Routine), or prints it (if a String). In the latter case, a newline is then output unless flag is true. +PrintCapitalised() does all that; the difference is that the first letter of any output is in upper +case unless nocaps is true.
+

Cap(string, nocaps) +

prints the string with the first letter in upper case, unless nocaps is true. Can also be used as a +print rule: +
  print ..., (Cap) myString, ...;
+

Centre(string)
+Centre(obj, prop)
+

prints a single-line string approximately centrally between the left and right borders of the screen +by preceding it with an appropriate number of spaces. The routine works only for monospaced +fonts (that is, after font off;), and is only likely to work well in the main Glulx window if the +normal style for TextBuffer uses a non-proportional font. It is however useful for centring +information in the status line. Can also be used as a print rule: +
  print ..., (Centre) myString, ...;
+

PrintOrRunVal(value, flag) +

if value refers to to an object, prints that object’s name; +if value refers to a routine, runs that routine; if value refers to a string, +prints that string (with a terminating newline unless flag is +true).
+

Bugs fixed

+Items of the form [L61036] quote the bug’s reference number in the ‘Library’ section of the Inform +Patch List. +
    +
  • A command like EMPTY ME no longer replies “yourself can't contain things”. [L61036] +

  • The commands TAKE ALL FROM X and REMOVE ALL FROM X, where X is a closed or +empty container, now produce sensible messages rather than “You can’t see any such thing” and +“You can’t use multiple objects with that verb” respectively. [L61035] +

  • A problem with the misbehaviour of name properties on rooms, in conjunction with THE, has been +corrected. [L61034] +

  • The command PUT X INTO X now correctly produces “You can’t put something inside itself”, +rather than “You can’t see any such thing”. [L61033] +

  • Run-time errors resulting from IndirectlyContains() attempting to find the parent of a Class +which supports dynamic creation of objects have been resolved. [L61032] +

  • Code in Parser__parse() which deals with looking ahead to the indirect object in cases like PUT +ALL INTO BAG (a MULTIEXCEPT token) and TAKE ALL FROM BAG (a MULTIINSIDE +token) now correctly sets the advance_warning global (to BAG). [L61031, L61023] +

  • The Inform Designer’s Manual (p. 98) states that SHOWOBJ should accept an object number; +now it does. [L61030] +

  • The YesOrNo() routine now re-prompts correctly after garbage input. [L61029] +

  • The parse buffer is no longer declared and initialised incorrectly (albeit harmlessly). [L61028, +L60708] +

  • The Inform Designer’s Manual (p. 93) defines the calling order of routines and properties for the +‘Before’ stage as follows:

    +

    1. GamePreRoutine() +
    2. orders of the player +
    3. react_before of every object in scope +
    4. before of the current room +
    5. before of the first noun, if specified

    +In the library, however, steps 3 and 4 are executed in reverse order. They are now as documented. +[L61027] +

  • A found_in floating object which the player is able to take (probably due to a coding error) is no +longer silently dropped when the player returns to one of the listed rooms. [L61026] +

  • A small problem with inherited describe properties has been corrected. [L61025] +

  • Standard screen-handling is now implemented in v6 games. [L61022] +

  • The handling of “You can’t go that way” messages is made consistent. Also, the statement +ChangeDefault(cant_go,myRoutine); now works. [L61020] +

  • Attempting to place an object in/on an object where it is already now results in “It’s already +there”, rather than “You need to be holding it before you can put it into something else”. [L61019] +

  • A problem with misleading inventory listing has been clarified. [L61018] +

  • The command LEAVE X now correctly produces “But you aren’t in/on the X”, if appropriate. +[L61017] +

  • The response to READ was inappropriate when an object is misspelled or out of scope. [L61016] +

  • A small bug in the choice of library messages for PUSH and TURN, which wasn’t noticeable +unless you overrode the messages to be different from PULL, has been corrected. [L61015] +

  • If you are in a dark room, you cannot examine what you are holding. Yet if you open a container +you brought in from a lit room, the standard message “You open the box, revealing a...” was not +being suppressed. [L61014] +

  • The ScoreMatchL() routine in Parserm.h incorrectly decided which objects meet descriptors. As a +result, some objects that didn’t meet descriptors were not properly removed from the match list +when the library is deciding which objects best match a player’s input. [L61013] +

  • The Infix problem parsing commands containing commas and periods has been fixed. +[L61010] +

  • A problem when describing what’s visible after opening a container has been corrected. [L61009] +

  • An inappropriate message after GO NORTH CIRCULAR has been corrected. [L61008] +

  • Modified foreground and background colours are now correct after RESTORE and UNDO. +[L61007] +

  • The grammar property now works with a large game whose dictionary lies above $8000. [L61006] +

  • A buffer conflict with disambiguation and UNDO has been resolved. [L61004] +

  • If a player is inside a closed, non-transparent container, the library prints an extra blank line +between the header “The container” and the first inside_description line it prints. No more. +[L61002] +

  • The list writing routines do not handle plural containers correctly. If you have two empty boxes, it +might list “two boxes (which is closed)”. Not only should it say “are closed”, but it will lump +empty containers together even if some are open and others aren’t. Now resolved. [L61001] +

  • A conflict between DrawStatusLine() and DisplayStatus() on how to determine whether to +display turns or time is settled in favour of checking a header flag. [L60709] +

+ +

Support for Glulx

+One of the limitations of the Z-machine is the size of the largest game that it supports: 256Kb if you +compile a version 5 game, or 512Kb if you compile for version 8. If you find yourself up against this +limit and you’ve tried all the standard tricks to save a few bytes here and there, then it’s time to switch +to Glulx. That’s easy to do: you just supply the -G compiler switch, the compiler generates Glulx code, +and any Glulx interpreter will be able to run it.

+Actually, it isn’t always quite that simple... +

Knowing which is which

+As mentioned earlier, the compiler automatically defines a couple of useful constants. If you’re +compiling for the Z-machine then TARGET_ZCODE is defined; if you’re compiling for Glulx then you’re +given TARGET_GLULX instead. You can use these with the Ifdef directive, like this: +
+  #Ifdef TARGET_ZCODE; 
+  ! Z-machine code here 
+  #Endif;
+  #Ifdef TARGET_GLULX; 
+  ! Equivalent Glulx code here 
+  #Endif; 
+
or more commonly like this:
+  #Ifdef TARGET_ZCODE; 
+  ! Z-machine code here 
+  #Ifnot;
+  ! Equivalent Glulx code here 
+  #Endif; 
+
You’ll find a lot of this if you look in the library files, but it’s less frequently needed in a source file. +

Glulx differences

+The two VMs are not identical, and you need to be aware of their differences. +

Word size

+The most basic difference between Glulx and the Z-machine is that words are four bytes long instead +of two. All Glulx variables are 32-bit values, the stack contains 32-bit values, property entries are +32-bit values, and so on.

+In most Inform programming, you don’t need to worry about this change at all. For example, if you +have an array +

  Array mylist --> 10;
+...then Z-code Inform allocates ten words — that is, twenty bytes — and you can access these values as +mylist-->0 through mylist-->9. If you compile the same code under Glulx Inform, the compiler again +allocates ten words — now forty bytes — and you can still access them as mylist-->0 through +mylist-->9. Everything works the same, except that the array can contain values greater than the +Z-machine’s limit of 65535.

+Table arrays also refer to two- or four-byte word values, and the first word is the length of the array. +String and -> arrays, and the -> notation, still refer to single bytes. You should not have to modify +your code here, either.

+There are two important cases where you will have to modify your code. First is the .# operator. The +expression obj.#prop returns the length of the property in bytes. Since properties almost always +contain words rather than bytes, it is very common to have Z-machine code like: +

+  len = (obj.#prop) / 2; 
+  for (i=0 : i<len : i++) 
+      print (string) (obj.&prop)-->i; 
+
+In Glulx Inform programs, it is necessary to divide by 4 instead of by 2, so you should replace the +above code with: +
+  len = (obj.#prop) / WORDSIZE; 
+  for (i=0 : i<len : i++) 
+   print (string) (obj.&prop)-->i; 
+
+This will compile and run correctly in both VMs.

+The other circumstance where your code may need modifying in this manner is when using the ‘print +to array’ feature. Code like this: +

+  Array mybuf buffer 100; ! must be big enough for largest string you'll print 
+  mystr = "hello"; 
+  mystr.print_to_array(mybuf);
+
+results in the first word of mybuf containing 5 (the number of characters in mystr), and the following +five bytes containing ‘h’, ‘e’, ‘l’, ‘l’ and ‘o’. +In the Z-machine, you could then output the characters from the array with either of these code fragments: +
+  len = 2 + mybuf-->0 
+  for (i=2 : i<len : i++)
+   print (char) mybuf->i; 
+
+  len = mybuf-->0 
+  for (i=0 : i<len : i++)
+   print (char) mybuf->(i+2);
+
+Again, you can make the code safe for both VMs if you change “2” to “WORDSIZE”: +
+  len = WORDSIZE + mybuf-->0 
+  for (i=WORDSIZE : i<len : i++)
+   print (char) mybuf->i; 
+
+  len = mybuf-->0 
+  for (i=0 : i<len : i++)
+   print (char) mybuf->(i+WORDSIZE);
+
+See also Features available only in Glulx for details of print_to_array under Glulx. +

Directives

+Glulx handles the majority of Inform directives, with two exceptions: +
    +
  • The Zcharacter directive causes a compilation error, since Glulx does not use the ZSCII character set. +Your best approach is to bypass the directive when compiling for Glulx: +

    +  #Ifdef TARGET_ZCODE; 
    +  Zcharacter ... ; 
    +  #Endif;
    +
    +
  • The (obsolete) Lowstring directive causes a run-time error when used like this: +

    +  Lowstring mystr "hello"; 
    +
    +  string 0 mystr; 
    +  print "@00 and goodbye.";
    +
    +This simpler form, avoiding the use of Lowstring, works successfully: +
    +  string 0 "hello"; 
    +  print "@00 and goodbye.";
    +
    +See also Features available only in Glulx for additional printing variable support. +
+

Statements

+Glulx handles the majority of Inform statements, with a few exceptions. If you try to use any of these +statements in Glulx, you will cause a compilation error: +
    +
  • save, restore: These are more complicated procedures in Glulx than in Z-code, and cannot be +implemented without involving library variables and routines. If you want to do this sort of thing, +modify or copy the library SaveSub() and RestoreSub() routines. +

  • read: Similarly, reading a line of text in Glulx involves the library; the compiler cannot generate +stand-alone code to do it. See instead the library KeyboardPrimitive() routine. +

  • @opcode: The Z-machine assembly language is completely different from that of Glulx. If you +have used any assembly instructions, you will need to conceal them when compiling for Glulx, as +described in Knowing which is which. See also +Features available only in Glulx for details of the glk() function call. +

+Glulx now supports the Inform style statement, mapping the Z-machine forms onto Glk styles: +
+ + + + + + + + +
Inform statementEquivalent Glk style
style roman;style_Normal
style reverse;style_Alert
style bold;style_Subheader
style underline;style_Emphasized
style fixed;style_Preformatted
+However, it is important to remember that, with Glulx, the appearance of a game is under the player’s +control — Glulx interpreters enable the font parameters associated with each style to be specified +at run-time. Therefore, you should use styles with caution, and if necessary alert the player to the +settings which your game expects. +

Character handling

+Unlike the Z-machine, which internally uses the ZSCII character set (see the Inform Designer’s +Manual Table 2 on p. 519), Glulx uses strings consisting of either 8-bit characters in the ISO +8859-1 (Latin-1) encoding (which is the same as ZSCII for character values 0-127, but different for +character values 128-255), or 32-bit Unicode characters. In general you don't need to worry about +which string representation is used: the compiler will figure it out for you. The impact is as follows: +
    +
  • @escape_sequence: Escape sequences such as @:a and @LL +(for “ä” and “£” respectively) are accepted identically by both VMs. +

  • @@decnum: The number is the character’s internal decimal value, so @@65 +is “A” in both VMs, but @@165 is “ï” in the Z-machine and “¥” in Glulx. +

  • @{hexnum}: The number is the character’s Unicode value, so @{41} is “A” +and @{EF} is “ï” in both VMs. However, @{A5} is “¥” in Glulx, +but causes a Z-machine compilation error because “¥” isn’t a ZSCII character. +

  • -Cn: The compiler switches -C1 through -C9 +specify that the source file uses the character set +defined by ISO 8859-1 through 8859-9 respectively. In the Z-machine, the switch also initialises +the higher ZSCII character set to appropriate values; this later feature is irrelevant when compiling for +Glulx. +

  • Dictionaries in Glulx Inform 6 games have to consist of only ISO 8859-1 characters at present. This is +being worked on, and the necessary compiler changes have been made, but further library work is still needed. +

+

Compiler switches

+We’ve already mentioned the -G command line switch, which causes the compiler to output code +for Glulx rather than for the Z-machine. There are a few other changes in this area (see the Inform +Designer’s Manual Table 3 on p. 521). +
+ + + + + + + + +
SwitchToMeaning
-koff/onincompatible with -G (output debugging information)
-v*3 to 8incompatible with -G (set Z-machine Version)
-Goff/oncompile for Glulx VM
-Hoff/onuse Huffman compression on Glulx strings (on by default)
-Xoff/onincompatible with -G (include Infix debugger)
+

Glulx additions

+Glulx offers some additional capabilities above those of the Z-machine: +

Features available only in Glulx

+
    +
  • For Glulx, print_to_array function requires two arguments, the first being the array to print to, +and the second the length of that array: +

      len = mystr.print_to_array(mybuf, 80);
    +This example writes no more than 76 characters into the array. If mybuf is an 80-byte array, you +can be sure it will not be overrun. (Do not try this with the second argument less than 4.)

    +The value written into mybuf-->0, and the value returned, are not limited to the number of +characters written; they represent the number of characters in the complete string. This means +that: +

      len = mystr.print_to_array(mybuf, 4);
    +is an ugly but perfectly legal way to find the length of a string. (And in this case, mybuf need only +be four bytes long.) +
  • Z-code Inform supports 32 printing variables, @00 to @31, which you can include in strings and +then set with the statement: +

      string num "value";
    +In Glulx, this limit is raised to 64. Furthermore, in Glulx you can set these variables to a +stand-alone routine as well as a string: +
    +  [ routine; print "value"; ]; 
    +
    +  string num routine;
    +
    +In this case, the routine is called with no arguments and the result discarded; you should print your +desired output inside the routine.

    +In Glulx, unlike Z-code, a printing variable string can itself contain @.. codes, allowing recursion. +You can nest this as deeply as you want. However, it is obviously a bad idea to cause an infinite +recursion. For example, this will certainly crash the interpreter: +

    +  string 3 "This is a @03!"; 
    +  print "What is @03?"; 
    +
    +
  • Many of the things that used to be Z-code assembly are now handled by Glk function calls. +Making a Glk function call from Inform is slightly awkward, but not difficult.

    +All of Glk is handled by the built-in Inform function glk(), which takes one or more arguments. +The first argument is an integer; this tells which Glk call in being invoked. The remaining +arguments are just the arguments to the Glk call, in order.

    +Say, for example, that you want to set the text style to “preformatted”. The Inform code to +accomplish this is: +

      glk($0086, 2);
    +The hex value $0086 means glk_set_style; the value “2” means Preformatted.

    +The table of Glk calls, and the integers that refer to them, is given in Section 12.1.6 of the Glk specification +(remember that the values given there are hexadecimal). +

    +Since calls based on numeric codes are not very easy to read, we recommend that you download +John Cater’s infglk.h library header, which defines wrapper functions and constants. For +example, you could achieve the same effect with this call: +

      glk_set_style(style_Preformatted);
    +When you read the Glk specification, bear in mind that the NULL value it talks about is the C +language NULL (0), not the Inform Library NULL (-1). infglk.h defines a constant GLK_NULL (equal +to 0) which you can use where appropriate. +
  • By default, arguments to routines work the same in Glulx as they do in Z-code. When you call a +routine, the arguments that you pass are written into the routine’s local variables, in order. If you +pass too many arguments, the extras are discarded; too few, and the unused local variables are +filled with zeroes.

    +However, the Glulx VM supports a second style of routine. You can define a routine of this type +by naming the first argument _vararg_count. For example: +

    +  [ StackFunc _vararg_count ix pos len; 
    +      !   Glulx code here
    +  ];
    +
    +If you do this, the routine arguments are not written into the local variables. Instead, they are +pushed onto the stack, and you must use Glulx assembly to pull them off. All the local variables +are initialized to zero, except for _vararg_count, which (as you might expect) contains the number +of arguments that were passed in.

    +Note that _vararg_count is a normal local variable, aside from its useful initial value. You can +assign to it, increment or decrement it, use it in expressions, and so on.

    +Stack-argument routines are most useful if you want a routine with variable arguments, or if you +want to write a wrapper that passes its arguments on to another routine. +

+

Library routines available only in Glulx

+

KeyCharPrimitive(win, nostat); +

If win is nonzero, the character input request goes to that Glk window +(instead of gg_mainwin, the +default.) If nostat is nonzero, any window rearrangement event is returned immediately as value +80000000 (instead of the default behavior, which is to call DrawStatusLine() and keep waiting.)
+

PrintAnything(thingie, ...); +

In the Z-machine, strings and routines are “packed” addresses, dictionary words are normal +addresses, and game objects are represented as sequential numbers from 1 to #top_object. These +ranges overlap; a string, a dictionary word, and an object could conceivably all be represented by +the same numeric value.

+In Glulx, all those things are represented by normal addresses, so different items will always have +different values. Furthermore, the first byte found at the address is an identifier value, which +specifies what kind of item the address contains.

+PrintAnything() prints any thingie — string, routine (with optional arguments), object, object +property (with optional arguments), or dictionary word — known to the library. +

+ + + + + + + + + + + + +
CallingIs equivalent to
PrintAnything()(nothing printed)
PrintAnything(0)(nothing printed)
PrintAnything(string)print (string) "string";
PrintAnything(dictionaryword)print (address) 'dictionaryword';
PrintAnything(obj)print (name) obj;
PrintAnything(obj, prop)obj.prop();
PrintAnything(obj, prop, args...)obj.prop(args...);
PrintAnything(routine)routine();
PrintAnything(routine, args...)routine(args...);
+Extra arguments after a string or dictionary word are safely ignored. +The (first) argument you pass in is always interpreted as a thingie reference, not as an integer. +This is why none of the forms shown above print out an integer. However, you can get the same +effect by calling +
  PrintAnything(DecimalNumber, num);
+...which is where the DecimalNumber() routine comes in handy. You can also, of course, use other +library routines, and do tricks like +
+  PrintAnything(EnglishNumber, num); 
+  PrintAnything(DefArt, obj);
+
+None of this may seem very useful; after all, there are already ways to print all those things. But +PrintAnything() is vital in implementing the following routine:
+

PrintAnyToArray(array, arraylen, thingie, ...); +

This works the same way, except that instead of printing to the screen, the output is diverted to the +given array.

+The first two arguments must be the array address and its maximum length. Up to that many +characters will be written into the array; any extras will be silently discarded. This means that you +do not have to worry about array overruns.

+The PrintAnyToArray() routine returns the number of characters generated. (This may be greater +than the length of the array. It represents the entire text that was output, not the limited number +written into the array.)

+It is safe to nest PrintAnyToArray() calls. That is, you can call PrintAnyToArray(routine), where +routine() itself calls PrintAnyToArray(). (However, if they try to write to the same array, chaos +will ensue.)

+It is legal for arraylen to be zero (in which case array is ignored, and may be zero as well.) This +discards all of the output, and simply returns the number of characters generated. You can use this +to find the length of anything — even a function call.

+

Entry points available only in Glulx

+An entry point is a routine which you can provide in your code, or leave out; the library will call it if +it’s present, ignore it if not — see §21 of the Inform Designer’s Manual.

+The library has some entry points which aid in writing more complicated interfaces — games with +sound, graphics, extra windows, and other fancy Glk tricks. If you’re just writing a standard +Infocom-style game, you can ignore this section. +

HandleGlkEvent(ev, context, abortres) +

This entry point is called every time a Glk event occurs. The event could indicate nearly anything: +a line of input from the player, a window resize or redraw event, a clock tick, a mouse click, or so +on.

+The library handles all the events necessary for a normal Infocom-style game. You need to supply +a HandleGlkEvent() routine only if you want to add extra functionality. The ev argument is a +four-word array which describes the event. ev-->0 is the type of the event; ev-->1 is the window +involved (if relevant); and ev-->2 and ev-->3 are extra information. +The context argument is 0 if +the event occurred during line input (normal commands, YesOrNo(), or some other use of the +KeyboardPrimitive() library routine); 1 indicates that the event occurred during character input +(any use of the KeyCharPrimitive() library routine). The abortres argument is used only if you +want to cancel player input and force a particular result; see below.

+If you return 2 from HandleGlkEvent(), player input will immediately be aborted. Some additional +code is also required: +

    +
  • If this was character input (context==1), you must call the Glk cancel_char_event function, +and then set abortres-->0 to the character you want returned. Then return 2; +KeyCharPrimitive() will end and return the character, as if the player had hit it. +

  • If this was line input (context==0), you must call the Glk cancel_line_event function. (You +can pass an array argument to see what the player had typed so far.) Then, fill in the length of +the input to be returned in abortres-->0. If this is nonzero, write the input characters +sequentially into the array starting at abortres-->WORDSIZE, up to (but not including) +abortres-->(WORDSIZE+len). Do not exceed 256 characters. Then return 2; +KeyboardPrimitive() will end and return the line. +

+If you return -1 from HandleGlkEvent(), player input will continue even after a keystroke (for +character input) or after the enter key (for line input). +You must re-request input by calling request_char_input or request_line_input. +Any other return value from HandleGlkEvent() (a normal return, rfalse, or rtrue) will not affect +the course of player input.
+

InitGlkWindow(winrock) +

This entry point is called by the library when it sets up the standard windows: the story window, +the status window, and (if you use quote boxes) the quote box window. The story and status +windows are created when the game starts (before Initialise()). The quote window is created +and destroyed as necessary.

+InitGlkWindow() is called in five phases: +

    +
  1. The library calls InitGlkWindow(0). This occurs at the very beginning of execution, +even before Initialise(). You can set up any situation you want. (However, remember +that the story and status windows might already exist — for example, if the player has +just typed RESTART.) This is a good time to set gg_statuswin_size to a value other +than 1. Return 0 to proceed with the standard library window setup, or 1 if you’ve +created all the windows yourself. +

  2. The library calls InitGlkWindow(GG_MAINWIN_ROCK), before creating the story window. +This is a good time to set up style hints for the story window. Return 0 to let the library +create the window; return 1 if you have yourself created a window and stored it in +gg_mainwin. +

  3. The library calls InitGlkWindow(GG_STATUSWIN_ROCK), before creating the status +window. Again, return 0 to let the library do it; return 1 if you have created a window +and stored it in gg_statuswin. +

  4. The library calls InitGlkWindow(1). This is the end of window setup; you can take this +opportunity to open other windows. (Or you can do that in your Initialise() routine. +It doesn’t matter much.) +

  5. The library calls InitGlkWindow(GG_QUOTEWIN_ROCK), before creating the quote box +window. This does not occur during game initialization; the quote box window is +created during the game, whenever you print a quote, and destroyed one turn later. As +usual, return 1 to indicate that you’ve created a window in gg_quotewin. (The desired +number of lines for the window can be found in gg_arguments-->0.) +

+However you handle window initialization, remember that the library requires a gg_mainwin. If +you don’t create one, and don’t allow the library to do so, the game will shut down. Contrariwise, +the status window and quote windows are optional; the library can get along without them.
+

IdentifyGlkObject(phase, type, ref, rock) +

This entry point is called by the library to let you know what Glk objects exist. You must supply +this routine if you create any windows, filerefs, file streams, or sound channels beyond the +standard library ones. (This is necessary because after a RESTORE, RESTART, or UNDO +command, your global variables containing Glk objects will be wrong.)

+IdentifyGlkObject() is called in three phases: +

    +
  1. The library calls IdentifyGlkObject() with phase==0. You should set all your Glk +object references to zero. +

  2. The library calls IdentifyGlkObject() with phase==1. This occurs once for each +window, stream, and fileref that the library doesn’t recognize. (The library handles the +two standard windows, and the files and streams that have to do with saving, +transcripts, and command records. You only have to deal with objects that you create.) +You should set whatever reference is appropriate to the object. For each object: type +will be 0, 1, 2 for windows, streams, filerefs respectively; ref will be the object +reference; and rock will be the object’s rock, by which you can recognize it. +

  3. The library calls IdentifyGlkObject() with phase==2. This occurs once, after all the +other calls, and gives you a chance to recognize objects that aren’t windows, streams, +or filerefs. If you don’t create any such objects, you can ignore that bit. But you should +also take the opportunity to update all your Glk objects to the game state that was just +started or restored. (For example, redraw graphics, or set the right background sounds +playing.) +

+

Information for translators

+The library is designed to be easily translatable, and there are currently versions available in French, +German and several other languages. If you maintain one of these translations, the following +information may be useful to you. +

English.h

+This is the “language definition file”, and in translation is replaced by French.h, German.h, etc. The +following changes have been made: +
    +
  • Class CompassDirection and its objects have been extensively revised. +

  • Routine LanguageVerb() has been reworked. +

  • Routines LanguageVerbIsDebugging(), LanguageVerbLikesAdverb() and LanguageVerbMayBeName() +have been added to isolate language-specific tests previously embedded in parserm.h. +

  • Constants YOU__TX and COMMA__TX have been added. +

  • Routine LanguageLM() has been sorted alphabetically. +

  • In that routine, CommandsOff, CommandsOn and CommandsRead have been added. +

  • Also in that routine, Exit, Inv, Look, Miscellany, Places, +Pronouns and Score have been extended, +and Go has been modified. +

  • Constant LIBRARY_ENGLISH has been added. We suggest that translated versions instead define +LIBRARY_DUTCH, LIBRARY_FRENCH, LIBRARY_GERMAN, LIBRARY_ITALIAN, LIBRARY_SPANISH, +LIBRARY_SWEDISH etc, in case it becomes useful at some time to determine which language is in +force. +

+

Grammar.h

+This file contains grammars for English verbs like TAKE and DROP, and in translation is replaced +by FrenchG.h, GermanG.h, etc. The following changes have been made: +
    +
  • The grammars have been sorted alphabetically. +

  • Verb definitions 'recording' and 'replay' are no longer conditional on DEBUG. +

  • Verb definitions 'showobj', 'ask', 'exit', 'look' and 'tell' have been extended. +

  • Verb definition 'pry' 'prise' etc has been added. +

  • Constant LIBRARY_GRAMMAR has been added. +

+ + + diff --git a/arrays.c b/arrays.c new file mode 100644 index 0000000..9cd5011 --- /dev/null +++ b/arrays.c @@ -0,0 +1,658 @@ +/* ------------------------------------------------------------------------- */ +/* "arrays" : Parses array declarations and constructs arrays from them; */ +/* likewise global variables, which are in some ways a */ +/* simpler form of the same thing. */ +/* */ +/* Part of Inform 6.33 */ +/* copyright (c) Graham Nelson 1993 - 2014 */ +/* */ +/* ------------------------------------------------------------------------- */ + +#include "header.h" + +/* ------------------------------------------------------------------------- */ +/* Arrays defined below: */ +/* */ +/* int dynamic_array_area[] Initial values for the bytes of */ +/* the dynamic array area */ +/* int32 global_initial_value[n] The initialised value of the nth */ +/* 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. */ + +int dynamic_array_area_size; /* Size in bytes */ + +int no_arrays; +int32 *array_symbols; +int *array_sizes, *array_types; + +static int array_entry_size, /* 1 for byte array, 2 for word array */ + array_base; /* Offset in dynamic array area of the + array being constructed. During the + same time, dynamic_array_area_size + is the offset of the initial entry + in the array: so for "table" and + "string" arrays, these numbers are + different (by 2 and 1 bytes resp) */ + + /* In Glulx, of course, that will be + 4 instead of 2. */ + +extern void finish_array(int32 i) +{ + /* Write the array size into the 0th byte/word of the array, if it's + a "table" or "string" array */ + if (!glulx_mode) { + + if (array_base!=dynamic_array_area_size) + { if (dynamic_array_area_size-array_base==2) + { dynamic_array_area[array_base] = i/256; + dynamic_array_area[array_base+1] = i%256; + } + else + { if (i>=256) + error("A 'string' array can have at most 256 entries"); + dynamic_array_area[array_base] = i; + } + } + + } + else { + if (array_base!=dynamic_array_area_size) + { if (dynamic_array_area_size-array_base==4) + { + dynamic_array_area[array_base] = (i >> 24) & 0xFF; + dynamic_array_area[array_base+1] = (i >> 16) & 0xFF; + dynamic_array_area[array_base+2] = (i >> 8) & 0xFF; + dynamic_array_area[array_base+3] = (i) & 0xFF; + } + else + { if (i>=256) + error("A 'string' array can have at most 256 entries"); + dynamic_array_area[array_base] = i; + } + } + + } + + /* Move on the dynamic array size so that it now points to the next + available free space */ + + dynamic_array_area_size += i*array_entry_size; + +} + +extern void array_entry(int32 i, assembly_operand VAL) +{ + if (!glulx_mode) { + /* Array entry i (initial entry has i=0) is set to Z-machine value j */ + + if (dynamic_array_area_size+(i+1)*array_entry_size > MAX_STATIC_DATA) + memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); + + if (array_entry_size==1) + { dynamic_array_area[dynamic_array_area_size+i] = (VAL.value)%256; + + if (VAL.marker != 0) + error("Entries in byte arrays and strings must be known constants"); + + /* If the entry is too large for a byte array, issue a warning + and truncate the value */ + else + if (VAL.value >= 256) + warning("Entry in '->', 'string' or 'buffer' array not in range 0 to 255"); + } + else + { dynamic_array_area[dynamic_array_area_size + 2*i] = (VAL.value)/256; + dynamic_array_area[dynamic_array_area_size + 2*i+1] = (VAL.value)%256; + if (VAL.marker != 0) + backpatch_zmachine(VAL.marker, DYNAMIC_ARRAY_ZA, + dynamic_array_area_size + 2*i); + } + } + else { + /* Array entry i (initial entry has i=0) is set to value j */ + + if (dynamic_array_area_size+(i+1)*array_entry_size > MAX_STATIC_DATA) + memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); + + if (array_entry_size==1) + { dynamic_array_area[dynamic_array_area_size+i] = (VAL.value) & 0xFF; + + if (VAL.marker != 0) + error("Entries in byte arrays and strings must be known constants"); + + /* If the entry is too large for a byte array, issue a warning + and truncate the value */ + else + if (VAL.value >= 256) + warning("Entry in '->', 'string' or 'buffer' array not in range 0 to 255"); + } + else if (array_entry_size==4) + { dynamic_array_area[dynamic_array_area_size + 4*i] = (VAL.value >> 24) & 0xFF; + dynamic_array_area[dynamic_array_area_size + 4*i+1] = (VAL.value >> 16) & 0xFF; + dynamic_array_area[dynamic_array_area_size + 4*i+2] = (VAL.value >> 8) & 0xFF; + dynamic_array_area[dynamic_array_area_size + 4*i+3] = (VAL.value) & 0xFF; + if (VAL.marker != 0) + backpatch_zmachine(VAL.marker, ARRAY_ZA, + dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES + 4*i); + } + else + { + error("Somehow created an array of shorts"); + } + } +} + +/* ------------------------------------------------------------------------- */ +/* Global and Array directives. */ +/* */ +/* Global | */ +/* | = */ +/* | */ +/* */ +/* Array */ +/* */ +/* where an array specification is: */ +/* */ +/* | -> | */ +/* | --> | ... */ +/* | string | [ [,] [;] ... ]; */ +/* | table */ +/* */ +/* ------------------------------------------------------------------------- */ + +extern void set_variable_value(int i, int32 v) +{ global_initial_value[i]=v; +} + +/* There are four ways to initialise arrays: */ + +#define UNSPECIFIED_AI -1 +#define NULLS_AI 0 +#define DATA_AI 1 +#define ASCII_AI 2 +#define BRACKET_AI 3 + +extern void make_global(int array_flag, int name_only) +{ + /* array_flag is TRUE for an Array directive, FALSE for a Global; + name_only is only TRUE for parsing an imported variable name, so + array_flag is always FALSE in that case. */ + + int32 i; + int array_type, data_type; + assembly_operand AO; + + int32 global_symbol; + const char *global_name; + debug_location_beginning beginning_debug_location = + get_token_location_beginning(); + + directive_keywords.enabled = FALSE; + get_next_token(); + i = token_value; + global_symbol = i; + global_name = token_text; + + if (!glulx_mode) { + if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T) + && (svals[i] >= LOWEST_SYSTEM_VAR_NUMBER)) + goto RedefinitionOfSystemVar; + } + else { + if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T)) + goto RedefinitionOfSystemVar; + } + + if ((token_type != SYMBOL_TT) || (!(sflags[i] & UNKNOWN_SFLAG))) + { discard_token_location(beginning_debug_location); + if (array_flag) + ebf_error("new array name", token_text); + else ebf_error("new global variable name", token_text); + panic_mode_error_recovery(); return; + } + + if ((!array_flag) && (sflags[i] & USED_SFLAG)) + error_named("Variable must be defined before use:", token_text); + + if (array_flag) + { + if (!glulx_mode) + assign_symbol(i, dynamic_array_area_size, ARRAY_T); + else + assign_symbol(i, + dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES, ARRAY_T); + if (no_arrays == MAX_ARRAYS) + memoryerror("MAX_ARRAYS", MAX_ARRAYS); + array_symbols[no_arrays] = i; + } + else + { if (!glulx_mode && no_globals==233) + { discard_token_location(beginning_debug_location); + error("All 233 global variables already declared"); + panic_mode_error_recovery(); + return; + } + if (glulx_mode && no_globals==MAX_GLOBAL_VARIABLES) + { discard_token_location(beginning_debug_location); + memoryerror("MAX_GLOBAL_VARIABLES", MAX_GLOBAL_VARIABLES); + panic_mode_error_recovery(); + return; + } + + variable_tokens[MAX_LOCAL_VARIABLES+no_globals] = i; + assign_symbol(i, MAX_LOCAL_VARIABLES+no_globals, GLOBAL_VARIABLE_T); + variable_tokens[svals[i]] = i; + + if (name_only) import_symbol(i); + else global_initial_value[no_globals++]=0; + } + + directive_keywords.enabled = TRUE; + + RedefinitionOfSystemVar: + + if (name_only) + { discard_token_location(beginning_debug_location); + return; + } + + get_next_token(); + + if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) + { if (array_flag) + { discard_token_location(beginning_debug_location); + ebf_error("array definition", token_text); + } + put_token_back(); + if (debugfile_switch && !array_flag) + { debug_file_printf(""); + debug_file_printf("%s", global_name); + debug_file_printf("
"); + write_debug_global_backpatch(svals[global_symbol]); + debug_file_printf("
"); + write_debug_locations + (get_token_location_end(beginning_debug_location)); + debug_file_printf("
"); + } + return; + } + + if (!array_flag) + { + if ((token_type == SEP_TT) && (token_value == SETEQUALS_SEP)) + { AO = parse_expression(CONSTANT_CONTEXT); + if (!glulx_mode) { + if (AO.marker != 0) + backpatch_zmachine(AO.marker, DYNAMIC_ARRAY_ZA, + 2*(no_globals-1)); + } + else { + if (AO.marker != 0) + backpatch_zmachine(AO.marker, GLOBALVAR_ZA, + 4*(no_globals-1)); + } + global_initial_value[no_globals-1] = AO.value; + if (debugfile_switch) + { debug_file_printf(""); + debug_file_printf("%s", global_name); + debug_file_printf("
"); + write_debug_global_backpatch(svals[global_symbol]); + debug_file_printf("
"); + write_debug_locations + (get_token_location_end(beginning_debug_location)); + debug_file_printf("
"); + } + return; + } + + obsolete_warning("more modern to use 'Array', not 'Global'"); + + if (!glulx_mode) { + backpatch_zmachine(ARRAY_MV, DYNAMIC_ARRAY_ZA, 2*(no_globals-1)); + global_initial_value[no_globals-1] + = dynamic_array_area_size+variables_offset; + } + else { + backpatch_zmachine(ARRAY_MV, GLOBALVAR_ZA, 4*(no_globals-1)); + global_initial_value[no_globals-1] + = dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES; + } + } + + array_type = BYTE_ARRAY; data_type = UNSPECIFIED_AI; + + if ((!array_flag) && + ((token_type==DIR_KEYWORD_TT)&&(token_value==DATA_DK))) + data_type=NULLS_AI; + else if ((!array_flag) && + ((token_type==DIR_KEYWORD_TT)&&(token_value==INITIAL_DK))) + data_type=DATA_AI; + else if ((!array_flag) && + ((token_type==DIR_KEYWORD_TT)&&(token_value==INITSTR_DK))) + data_type=ASCII_AI; + + else if ((token_type==SEP_TT)&&(token_value==ARROW_SEP)) + array_type = BYTE_ARRAY; + else if ((token_type==SEP_TT)&&(token_value==DARROW_SEP)) + array_type = WORD_ARRAY; + else if ((token_type==DIR_KEYWORD_TT)&&(token_value==STRING_DK)) + array_type = STRING_ARRAY; + else if ((token_type==DIR_KEYWORD_TT)&&(token_value==TABLE_DK)) + array_type = TABLE_ARRAY; + else if ((token_type==DIR_KEYWORD_TT)&&(token_value==BUFFER_DK)) + array_type = BUFFER_ARRAY; + else + { discard_token_location(beginning_debug_location); + if (array_flag) + ebf_error + ("'->', '-->', 'string', 'table' or 'buffer'", token_text); + else + ebf_error + ("'=', '->', '-->', 'string', 'table' or 'buffer'", token_text); + panic_mode_error_recovery(); + return; + } + + array_entry_size=1; + if ((array_type==WORD_ARRAY) || (array_type==TABLE_ARRAY)) + array_entry_size=WORDSIZE; + + get_next_token(); + if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) + { discard_token_location(beginning_debug_location); + error("No array size or initial values given"); + put_token_back(); + return; + } + + switch(data_type) + { case UNSPECIFIED_AI: + if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) + data_type = BRACKET_AI; + else + { data_type = NULLS_AI; + if (token_type == DQ_TT) data_type = ASCII_AI; + get_next_token(); + if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) + data_type = DATA_AI; + put_token_back(); + put_token_back(); + } + break; + case NULLS_AI: obsolete_warning("use '->' instead of 'data'"); break; + case DATA_AI: obsolete_warning("use '->' instead of 'initial'"); break; + case ASCII_AI: obsolete_warning("use '->' instead of 'initstr'"); break; + } + + array_base = dynamic_array_area_size; + + /* Leave room to write the array size in later, if string/table array */ + + if ((array_type==STRING_ARRAY) || (array_type==TABLE_ARRAY)) + dynamic_array_area_size += array_entry_size; + if (array_type==BUFFER_ARRAY) + dynamic_array_area_size += WORDSIZE; + array_types[no_arrays] = array_type; + + switch(data_type) + { + case NULLS_AI: + + AO = parse_expression(CONSTANT_CONTEXT); + + CalculatedArraySize: + + if (module_switch && (AO.marker != 0)) + { error("Array sizes must be known now, not externally defined"); + break; + } + + if (!glulx_mode) { + if ((AO.value <= 0) || (AO.value >= 32768)) + { error("An array must have between 1 and 32767 entries"); + AO.value = 1; + } + } + else { + if (AO.value <= 0 || (AO.value & 0x80000000)) + { error("An array may not have 0 or fewer entries"); + AO.value = 1; + } + } + + { for (i=0; i= 256) + { + error("Unicode characters beyond Latin-1 are not yet supported in Glulx array literals"); + } + else + { + chars.value = unicode; + } + } + else /* Z-code */ + { + zscii = unicode_to_zscii(unicode); + if ((zscii != 5) && (zscii < 0x100)) chars.value = zscii; + else + { unicode_char_error("Character can only be used if declared in \ +advance as part of 'Zcharacter table':", unicode); + chars.value = '?'; + } + } + chars.marker = 0; + set_constant_ot(&chars); + array_entry(i, chars); + } + } + break; + + case BRACKET_AI: + + /* In this case the array is initialised to the sequence of + constant values given over a whole range of compiler-lines, + between square brackets [ and ] */ + + i = 0; + while (TRUE) + { get_next_token(); + if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) + continue; + if ((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)) + break; + if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) + { /* Minimal error recovery: we assume that a ] has + been missed, and the programmer is now starting + a new routine */ + + ebf_error("']'", token_text); + put_token_back(); break; + } + put_token_back(); + array_entry(i, parse_expression(ARRAY_CONTEXT)); + i++; + } + } + + finish_array(i); + + if (debugfile_switch) + { debug_file_printf(""); + debug_file_printf("%s", global_name); + debug_file_printf(""); + write_debug_array_backpatch(svals[global_symbol]); + debug_file_printf(""); + debug_file_printf + ("%d", + dynamic_array_area_size - array_base); + debug_file_printf + ("%d", + array_entry_size); + debug_file_printf + ("%s", + (array_type == STRING_ARRAY || array_type == TABLE_ARRAY) ? + "true" : "false"); + get_next_token(); + write_debug_locations(get_token_location_end(beginning_debug_location)); + put_token_back(); + debug_file_printf(""); + } + + if ((array_type==BYTE_ARRAY) || (array_type==WORD_ARRAY)) i--; + if (array_type==BUFFER_ARRAY) i+=WORDSIZE-1; + array_sizes[no_arrays++] = i; +} + +extern int32 begin_table_array(void) +{ + /* The "box" statement needs to be able to construct (static) table + arrays of strings like this */ + + array_base = dynamic_array_area_size; + array_entry_size = WORDSIZE; + + /* Leave room to write the array size in later */ + + dynamic_array_area_size += array_entry_size; + + if (!glulx_mode) + return array_base; + else + return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; +} + +extern int32 begin_word_array(void) +{ + /* The "random(a, b, ...)" function needs to be able to construct + (static) word arrays like this */ + + array_base = dynamic_array_area_size; + array_entry_size = WORDSIZE; + + if (!glulx_mode) + return array_base; + else + return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; +} + +/* ========================================================================= */ +/* Data structure management routines */ +/* ------------------------------------------------------------------------- */ + +extern void init_arrays_vars(void) +{ dynamic_array_area = NULL; + global_initial_value = NULL; + array_sizes = NULL; array_symbols = NULL; array_types = NULL; +} + +extern void arrays_begin_pass(void) +{ no_arrays = 0; + if (!glulx_mode) + no_globals=0; + else + no_globals=11; + dynamic_array_area_size = WORDSIZE * MAX_GLOBAL_VARIABLES; +} + +extern void arrays_allocate_arrays(void) +{ dynamic_array_area = my_calloc(sizeof(int), MAX_STATIC_DATA, + "static data"); + array_sizes = my_calloc(sizeof(int), MAX_ARRAYS, "array sizes"); + array_types = my_calloc(sizeof(int), MAX_ARRAYS, "array types"); + array_symbols = my_calloc(sizeof(int32), MAX_ARRAYS, "array symbols"); + global_initial_value = my_calloc(sizeof(int32), MAX_GLOBAL_VARIABLES, + "global values"); +} + +extern void arrays_free_arrays(void) +{ my_free(&dynamic_array_area, "static data"); + my_free(&global_initial_value, "global values"); + my_free(&array_sizes, "array sizes"); + my_free(&array_types, "array sizes"); + my_free(&array_symbols, "array sizes"); +} + +/* ========================================================================= */ diff --git a/asm.c b/asm.c new file mode 100644 index 0000000..2fd12c7 --- /dev/null +++ b/asm.c @@ -0,0 +1,3172 @@ +/* ------------------------------------------------------------------------- */ +/* "asm" : The Inform assembler */ +/* */ +/* Part of Inform 6.33 */ +/* copyright (c) Graham Nelson 1993 - 2014 */ +/* */ +/* ------------------------------------------------------------------------- */ + +#include "header.h" + +uchar *zcode_holding_area; /* Area holding code yet to be transferred + to either zcode_area or temp file no 1 */ +uchar *zcode_markers; /* Bytes holding marker values for this + code */ +static int zcode_ha_size; /* Number of bytes in holding area */ + +memory_block zcode_area; /* Block to hold assembled code (if + temporary files are not being used) */ + +int32 zmachine_pc; /* PC position of assembly (byte offset + from start of Z-code area) */ + +int32 no_instructions; /* Number of instructions assembled */ +int execution_never_reaches_here, /* TRUE if the current PC value in the + code area cannot be reached: e.g. if + the previous instruction was a "quit" + opcode and no label is set to here */ + next_label, /* Used to count the labels created all + over Inform in current routine, from 0 */ + next_sequence_point; /* Likewise, for sequence points */ +int no_sequence_points; /* Kept for statistics purposes only */ + +static int label_moved_error_already_given; + /* When one label has moved, all subsequent + ones probably have too, and this flag + suppresses the runaway chain of error + messages which would otherwise result */ + +int sequence_point_follows; /* Will the next instruction assembled */ + /* be at a sequence point in the routine? */ + +int uses_unicode_features; /* Makes use of Glulx Unicode (3.0) + features? */ +int uses_memheap_features; /* Makes use of Glulx mem/heap (3.1) + features? */ +int uses_acceleration_features; /* Makes use of Glulx acceleration (3.1.1) + features? */ +int uses_float_features; /* Makes use of Glulx floating-point (3.1.2) + features? */ + +debug_location statement_debug_location; + /* Location of current statement */ + + +int32 *variable_tokens; /* The allocated size is + (MAX_LOCAL_VARIABLES + + MAX_GLOBAL_VARIABLES). The entries + MAX_LOCAL_VARIABLES and up give the + symbol table index for the names of + the global variables */ +int *variable_usage; /* TRUE if referred to, FALSE otherwise */ + +assembly_instruction AI; /* A structure used to hold the full + specification of a single Z-code + instruction: effectively this is the + input to the routine + assemble_instruction() */ + +static char opcode_syntax_string[128]; /* Text buffer holding the correct + syntax for an opcode: used to produce + helpful assembler error messages */ + +static int routine_symbol; /* The symbol index of the routine currently + being compiled */ +static char *routine_name; /* The name of the routine currently being + compiled */ +static int routine_locals; /* The number of local variables used by + the routine currently being compiled */ + +static int32 routine_start_pc; + +int32 *named_routine_symbols; + +static void transfer_routine_z(void); +static void transfer_routine_g(void); + +/* ------------------------------------------------------------------------- */ +/* Label data */ +/* ------------------------------------------------------------------------- */ + +static int first_label, last_label; +static int32 *label_offsets; /* Double-linked list of label offsets */ +static int *label_next, /* (i.e. zmachine_pc values) in PC order */ + *label_prev; +static int32 *label_symbols; /* Symbol numbers if defined in source */ + +static int *sequence_point_labels; + /* Label numbers for each */ +static debug_location *sequence_point_locations; + /* Source code references for each */ + /* (used for making debugging file) */ + +static void set_label_offset(int label, int32 offset) +{ + if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS); + + label_offsets[label] = offset; + if (last_label == -1) + { label_prev[label] = -1; + first_label = label; + } + else + { label_prev[label] = last_label; + label_next[last_label] = label; + } + last_label = label; + label_next[label] = -1; + label_symbols[label] = -1; +} + +/* ------------------------------------------------------------------------- */ +/* Useful tool for building operands */ +/* ------------------------------------------------------------------------- */ + +extern void set_constant_ot(assembly_operand *AO) +{ + if (!glulx_mode) { + if (AO->value >= 0 && AO->value <= 255) + AO->type = SHORT_CONSTANT_OT; + else + AO->type = LONG_CONSTANT_OT; + } + else { + if (AO->value == 0) + AO->type = ZEROCONSTANT_OT; + else if (AO->value >= -0x80 && AO->value < 0x80) + AO->type = BYTECONSTANT_OT; + else if (AO->value >= -0x8000 && AO->value < 0x8000) + AO->type = HALFCONSTANT_OT; + else + AO->type = CONSTANT_OT; + } +} + +extern int is_constant_ot(int otval) +{ + if (!glulx_mode) { + return ((otval == LONG_CONSTANT_OT) + || (otval == SHORT_CONSTANT_OT)); + } + else { + return ((otval == CONSTANT_OT) + || (otval == HALFCONSTANT_OT) + || (otval == BYTECONSTANT_OT) + || (otval == ZEROCONSTANT_OT)); + } +} + +extern int is_variable_ot(int otval) +{ + if (!glulx_mode) { + return (otval == VARIABLE_OT); + } + else { + return ((otval == LOCALVAR_OT) + || (otval == GLOBALVAR_OT)); + } +} + +/* ------------------------------------------------------------------------- */ +/* Used in printing assembly traces */ +/* ------------------------------------------------------------------------- */ + +extern char *variable_name(int32 i) +{ + if (i==0) return("sp"); + if (i= 256 && i < 286) + { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256]; + return ""; + } + } + else { + switch (i - MAX_LOCAL_VARIABLES) { + case 0: return "temp_global"; + case 1: return "temp__global2"; + case 2: return "temp__global3"; + case 3: return "temp__global4"; + case 4: return "self"; + case 5: return "sender"; + case 6: return "sw__var"; + case 7: return "sys__glob0"; + case 8: return "sys__glob1"; + case 9: return "sys__glob2"; + case 10: return "sys_statusline_flag"; + } + } + + return ((char *) symbs[variable_tokens[i]]); +} + +static void print_operand_z(assembly_operand o) +{ switch(o.type) + { case EXPRESSION_OT: printf("expr_"); break; + case LONG_CONSTANT_OT: printf("long_"); break; + case SHORT_CONSTANT_OT: printf("short_"); break; + case VARIABLE_OT: + if (o.value==0) { printf("sp"); return; } + printf("%s", variable_name(o.value)); return; + case OMITTED_OT: printf(""); return; + } + printf("%d", o.value); +} + +static void print_operand_g(assembly_operand o) +{ + switch (o.type) { + case EXPRESSION_OT: printf("expr_"); break; + case CONSTANT_OT: printf("long_"); break; + case HALFCONSTANT_OT: printf("short_"); break; + case BYTECONSTANT_OT: printf("byte_"); break; + case ZEROCONSTANT_OT: printf("zero_"); return; + case DEREFERENCE_OT: printf("*"); break; + case GLOBALVAR_OT: + printf("%s (global_%d)", variable_name(o.value), o.value); + return; + case LOCALVAR_OT: + if (o.value == 0) + printf("stackptr"); + else + printf("%s (local_%d)", variable_name(o.value), o.value-1); + return; + case SYSFUN_OT: + if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS) + printf("%s", system_functions.keywords[o.value]); + else + printf(""); + return; + case OMITTED_OT: printf(""); return; + default: printf("???_"); break; + } + printf("%d", o.value); +} + +extern void print_operand(assembly_operand o) +{ + if (!glulx_mode) + print_operand_z(o); + else + print_operand_g(o); +} + +/* ------------------------------------------------------------------------- */ +/* Writing bytes to the code area */ +/* ------------------------------------------------------------------------- */ + +static void byteout(int32 i, int mv) +{ if (zcode_ha_size >= MAX_ZCODE_SIZE) + memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE); + zcode_markers[zcode_ha_size] = (uchar) mv; + zcode_holding_area[zcode_ha_size++] = (uchar) i; + zmachine_pc++; +} + +/* ------------------------------------------------------------------------- */ +/* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */ +/* And of the however-many-there-are Glulx opcode */ +/* ------------------------------------------------------------------------- */ + +typedef struct opcodez +{ uchar *name; /* Lower case standard name */ + int version1; /* Valid from this version number... */ + int version2; /* ...until this one (or forever if this is 0) */ + int extension; /* In later versions, see this line in extension table: + if -1, the opcode is illegal in later versions */ + int code; /* Opcode number within its operand-number block */ + int flags; /* Flags (see below) */ + int op_rules; /* Any unusual operand rule applying (see below) */ + int flags2_set; /* If not zero, set this bit in Flags 2 in the header + of any game using the opcode */ + int no; /* Number of operands (see below) */ +} opcodez; + +typedef struct opcodeg +{ uchar *name; /* Lower case standard name */ + int32 code; /* Opcode number */ + int flags; /* Flags (see below) */ + int op_rules; /* Any unusual operand rule applying (see below) */ + int no; /* Number of operands */ +} opcodeg; + + /* Flags which can be set */ + +#define St 1 /* Store */ +#define Br 2 /* Branch */ +#define Rf 4 /* "Return flag": execution never continues after this + opcode (e.g., is a return or unconditional jump) */ +#define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */ + + /* 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 */ +#define LABEL 3 /* One operand, a label, given as long constant offset */ +#define CALL 4 /* First operand is name of a routine, to be assembled + 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 */ +#define GOP_MemHeap 2 /* uses_memheap_features */ +#define GOP_Acceleration 4 /* uses_acceleration_features */ +#define GOP_Float 8 /* uses_float_features */ + + /* Codes for the number of operands */ + +#define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */ +#define VAR 2 /* 0 to 4 */ +#define VAR_LONG 3 /* 0 to 8 */ +#define ONE 4 /* 1 */ +#define ZERO 5 /* 0 */ +#define EXT 6 /* Extended opcode set VAR: 0 to 4 */ +#define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */ + +static opcodez opcodes_table_z[] = +{ + /* Opcodes introduced in Version 3 */ + +/* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO }, +/* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO }, +/* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO }, +/* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO }, +/* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO }, +/* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO }, +/* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO }, +/* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO }, +/* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO }, +/* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO }, +/* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO }, +/* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO }, +/* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO }, +/* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO }, +/* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO }, +/* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO }, +/* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO }, +/* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO }, +/* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO }, +/* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO }, +/* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO }, +/* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO }, +/* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO }, +/* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO }, +/* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR }, +/* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR }, +/* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR }, +/* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR }, + /* This is the version of "read" called "sread" internally: */ +/* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR }, +/* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR }, +/* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR }, +/* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR }, +/* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR }, +/* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR }, +/* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR }, +/* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR }, +/* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR }, +/* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR }, +/* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR }, +/* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE }, +/* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE }, +/* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE }, +/* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE }, +/* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE }, +/* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE }, +/* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE }, +/* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE }, +/* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE }, +/* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE }, +/* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE }, +/* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE }, +/* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE }, +/* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE }, +/* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE }, +/* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO }, +/* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO }, +/* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO }, +/* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO }, +/* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO }, +/* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO }, +/* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO }, +/* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO }, +/* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO }, +/* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO }, +/* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO }, +/* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO }, +/* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO }, +/* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO }, + + /* Opcodes introduced in Version 4 */ + +/* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO }, +/* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR }, + /* This is the version of "read" called "aread" internally: */ +/* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR }, +/* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0, + VAR_LONG }, +/* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR }, +/* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR }, +/* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR }, +/* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR }, +/* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR }, +/* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR }, +/* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR }, +/* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR }, +/* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE }, + + /* Opcodes introduced in Version 5 */ + +/* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO }, +/* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO }, +/* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO }, +/* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR }, +/* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0, + VAR_LONG }, +/* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR }, +/* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR }, +/* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR }, +/* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR }, +/* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR }, +/* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE }, +/* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO }, +/* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO }, +/* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT }, +/* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT }, +/* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT }, +/* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT }, +/* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT }, + + /* Opcodes introduced in Version 6 */ + +/* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT }, +/* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT }, +/* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT }, +/* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT }, +/* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT }, +/* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT }, +/* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT }, +/* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT }, +/* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT }, +/* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT }, +/* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT }, +/* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT }, +/* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT }, +/* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT }, +/* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT }, +/* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT }, +/* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT }, + + /* Opcodes introduced in Z-Machine Specification Standard 1.0 */ + +/* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT }, +/* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT } +}; + + /* Subsequent forms for opcodes whose meaning changes with version */ + +static opcodez extension_table_z[] = +{ +/* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE }, +/* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO }, +/* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO }, +/* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR }, +/* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT }, +/* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT }, +/* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR } +}; + +static opcodez invalid_opcode_z = + { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO}; + +static opcodez custom_opcode_z; + +/* Note that this table assumes that all opcodes have at most two + branch-label or store operands, and that if they exist, they are the + last operands. Glulx does not actually guarantee this. But it is + true for all opcodes in the current Glulx spec, so we will assume + it for now. + + Also note that Inform can only compile branches to constant offsets, + even though the Glulx machine can handle stack or memory-loaded + operands in a branch instruction. +*/ + +static opcodeg opcodes_table_g[] = { + { (uchar *) "nop", 0x00, 0, 0, 0 }, + { (uchar *) "add", 0x10, St, 0, 3 }, + { (uchar *) "sub", 0x11, St, 0, 3 }, + { (uchar *) "mul", 0x12, St, 0, 3 }, + { (uchar *) "div", 0x13, St, 0, 3 }, + { (uchar *) "mod", 0x14, St, 0, 3 }, + { (uchar *) "neg", 0x15, St, 0, 2 }, + { (uchar *) "bitand", 0x18, St, 0, 3 }, + { (uchar *) "bitor", 0x19, St, 0, 3 }, + { (uchar *) "bitxor", 0x1A, St, 0, 3 }, + { (uchar *) "bitnot", 0x1B, St, 0, 2 }, + { (uchar *) "shiftl", 0x1C, St, 0, 3 }, + { (uchar *) "sshiftr", 0x1D, St, 0, 3 }, + { (uchar *) "ushiftr", 0x1E, St, 0, 3 }, + { (uchar *) "jump", 0x20, Br|Rf, 0, 1 }, + { (uchar *) "jz", 0x22, Br, 0, 2 }, + { (uchar *) "jnz", 0x23, Br, 0, 2 }, + { (uchar *) "jeq", 0x24, Br, 0, 3 }, + { (uchar *) "jne", 0x25, Br, 0, 3 }, + { (uchar *) "jlt", 0x26, Br, 0, 3 }, + { (uchar *) "jge", 0x27, Br, 0, 3 }, + { (uchar *) "jgt", 0x28, Br, 0, 3 }, + { (uchar *) "jle", 0x29, Br, 0, 3 }, + { (uchar *) "jltu", 0x2A, Br, 0, 3 }, + { (uchar *) "jgeu", 0x2B, Br, 0, 3 }, + { (uchar *) "jgtu", 0x2C, Br, 0, 3 }, + { (uchar *) "jleu", 0x2D, Br, 0, 3 }, + { (uchar *) "call", 0x30, St, 0, 3 }, + { (uchar *) "return", 0x31, Rf, 0, 1 }, + { (uchar *) "catch", 0x32, Br|St, 0, 2 }, + { (uchar *) "throw", 0x33, Rf, 0, 2 }, + { (uchar *) "tailcall", 0x34, Rf, 0, 2 }, + { (uchar *) "copy", 0x40, St, 0, 2 }, + { (uchar *) "copys", 0x41, St, 0, 2 }, + { (uchar *) "copyb", 0x42, St, 0, 2 }, + { (uchar *) "sexs", 0x44, St, 0, 2 }, + { (uchar *) "sexb", 0x45, St, 0, 2 }, + { (uchar *) "aload", 0x48, St, 0, 3 }, + { (uchar *) "aloads", 0x49, St, 0, 3 }, + { (uchar *) "aloadb", 0x4A, St, 0, 3 }, + { (uchar *) "aloadbit", 0x4B, St, 0, 3 }, + { (uchar *) "astore", 0x4C, 0, 0, 3 }, + { (uchar *) "astores", 0x4D, 0, 0, 3 }, + { (uchar *) "astoreb", 0x4E, 0, 0, 3 }, + { (uchar *) "astorebit", 0x4F, 0, 0, 3 }, + { (uchar *) "stkcount", 0x50, St, 0, 1 }, + { (uchar *) "stkpeek", 0x51, St, 0, 2 }, + { (uchar *) "stkswap", 0x52, 0, 0, 0 }, + { (uchar *) "stkroll", 0x53, 0, 0, 2 }, + { (uchar *) "stkcopy", 0x54, 0, 0, 1 }, + { (uchar *) "streamchar", 0x70, 0, 0, 1 }, + { (uchar *) "streamnum", 0x71, 0, 0, 1 }, + { (uchar *) "streamstr", 0x72, 0, 0, 1 }, + { (uchar *) "gestalt", 0x0100, St, 0, 3 }, + { (uchar *) "debugtrap", 0x0101, 0, 0, 1 }, + { (uchar *) "getmemsize", 0x0102, St, 0, 1 }, + { (uchar *) "setmemsize", 0x0103, St, 0, 2 }, + { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 }, + { (uchar *) "random", 0x0110, St, 0, 2 }, + { (uchar *) "setrandom", 0x0111, 0, 0, 1 }, + { (uchar *) "quit", 0x0120, Rf, 0, 0 }, + { (uchar *) "verify", 0x0121, St, 0, 1 }, + { (uchar *) "restart", 0x0122, 0, 0, 0 }, + { (uchar *) "save", 0x0123, St, 0, 2 }, + { (uchar *) "restore", 0x0124, St, 0, 2 }, + { (uchar *) "saveundo", 0x0125, St, 0, 1 }, + { (uchar *) "restoreundo", 0x0126, St, 0, 1 }, + { (uchar *) "protect", 0x0127, 0, 0, 2 }, + { (uchar *) "glk", 0x0130, St, 0, 3 }, + { (uchar *) "getstringtbl", 0x0140, St, 0, 1 }, + { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 }, + { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 }, + { (uchar *) "setiosys", 0x0149, 0, 0, 2 }, + { (uchar *) "linearsearch", 0x0150, St, 0, 8 }, + { (uchar *) "binarysearch", 0x0151, St, 0, 8 }, + { (uchar *) "linkedsearch", 0x0152, St, 0, 7 }, + { (uchar *) "callf", 0x0160, St, 0, 2 }, + { (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 }, + { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 }, + { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 }, + { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 }, + { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 }, + { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 }, + { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 }, + { (uchar *) "numtof", 0x190, St, GOP_Float, 2 }, + { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 }, + { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 }, + { (uchar *) "ceil", 0x198, St, GOP_Float, 2 }, + { (uchar *) "floor", 0x199, St, GOP_Float, 2 }, + { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 }, + { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 }, + { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 }, + { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 }, + { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 }, + { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 }, + { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 }, + { (uchar *) "log", 0x1AA, St, GOP_Float, 2 }, + { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 }, + { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 }, + { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 }, + { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 }, + { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 }, + { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 }, + { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 }, + { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 }, + { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 }, + { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 }, + { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 }, + { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 }, + { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 }, + { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 }, + { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 }, + { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 }, +}; + +/* The opmacros table is used for fake opcodes. The opcode numbers are + ignored; this table is only used for argument parsing. */ +static opcodeg opmacros_table_g[] = { + { (uchar *) "pull", 0, St, 0, 1 }, + { (uchar *) "push", 0, 0, 0, 1 }, +}; + +static opcodeg custom_opcode_g; + +static opcodez internal_number_to_opcode_z(int32 i) +{ opcodez x; + ASSERT_ZCODE(); + if (i == -1) return custom_opcode_z; + x = opcodes_table_z[i]; + if (instruction_set_number < x.version1) return invalid_opcode_z; + if (x.version2 == 0) return x; + if (instruction_set_number <= x.version2) return x; + i = x.extension; + if (i < 0) return invalid_opcode_z; + x = extension_table_z[i]; + if (instruction_set_number < x.version1) return invalid_opcode_z; + if (x.version2 == 0) return x; + if (instruction_set_number <= x.version2) return x; + return extension_table_z[x.extension]; +} + +static void make_opcode_syntax_z(opcodez opco) +{ char *p = "", *q = opcode_syntax_string; + sprintf(q, "%s", opco.name); + switch(opco.no) + { case ONE: p=" "; break; + case TWO: p=" "; break; + case EXT: + case VAR: p=" <0 to 4 operands>"; break; + case VAR_LONG: p=" <0 to 8 operands>"; break; + } + switch(opco.op_rules) + { case TEXT: sprintf(q+strlen(q), " "); return; + case LABEL: sprintf(q+strlen(q), "