A brief summary of A-code, MLA version 11.03

(Document date 07-Oct-2000)

NB: this description is by now incomplete; I will try to find the time to add other features introduced during the development of adv770. Though to be honest, it really needs a complete re-write! In any case, the best way to learn A-code is to study A-code sources, as I did. MLA 18 Dec 2001.

Contents


Introduction

A-code is the adventure-writing language developed by Dave Platt for the adv550 superset of the original Adventure, and subsequently developed by myself, in working on the adv660 superset.

A-code is not intended to be a programming language and hence as a programming language it is fairly primitive. Its strength are elsewhere - in supporting features such as text-switches or text-nesting. It also comes with a pre-programmed "skeleton" adventure template. This is written in A-code and implements lots of basic adventuring functionality, so that on the one hand you can get on with the meaty bits of your adventure straight away, while on the other if you want to give the standard parts a much more customised look, you can easily do that too. As a further bonus, each game created with the A-code system is a stand-alone package, requiring no separate game engine/interpreter.

Technically, A-code is a Polish notation (i.e. "prefix", i.e. "operator [argument [...]]") language. The original version by Dave Platt was organised as a "munger", translating A-code source into tokenised pseudo-binary, and an interpreter, interpreting the pseudo-binary at run time. This was an economical arrangement, but as Adventure4 grew in size, its performance on a multi-user machine gradually became less satisfactory. (We are talking mid-80s here! :-)

This is why this implementation of A-code takes a different route. It consists of an A-code to C translator (".acd -> .c" or just acdc!) and a C kernel which gets compiled and loaded with the translated code. The overall size of the program is larger, but the performance is very much better.

A-code is a deceptively simple language. Many of its features are quite primitive (e.g. global variables only, no procedure arguments...), but don't let this fool you. It is admirably suited to its purpose, making adventure development a much easier task than many other such tools. This is particularly true of this version, which comes with a pre-programmed "skeleton" adventure, already containing all generic adventure code for you to add to or modify.


A-code components

A-code source consists of (surprise!) declarations and code - in that order! Forward references are not allowed. Declarations declare a variety of entities and code manipulates them. Entities manipulated by A-code are variables, verbs, objects, places, texts, procedures, flags, values and words. All entities have the global scope. I.e. there are NO local variables or procedure arguments. (No longer the case, A-code 11 now supports local variables and procedure arguments.)

Flags are binary yes/no properties, referred to by symbolic names. There are three separate sets of these. One set is defined for objects, one for places and one for verbs/variables. Each entity has its own instance of the appropriate kind of a flag set. There are no limits on the number of flags in a flag set. Some flags are mandatory, (e.g. the object SCHIZOID flag, see below) but any number of additional ones can be declared in any of the three flag sets.

Even within a given set, each individual flag can have several different names, allowing for the greater legibility of the source. Since alternative names are usually required for variable flags, several different variable flag sets can be defined (as opposed to only one for each the object and the place flag sets). The actual variable flag set is simply the biggest of these, with individual flags having synonymous names as defined in different set definitions. Don't worry if this makes little sense - it will eventually.

Some entities (places, objects and variables) each have a state (a.k.a. value) associated with them. A state is simply a short signed integer. To enhance the legibility of A-code source, specific state values can be given symbolic names, e.g. making it possible to test a lamp against being "lit" instead of having the value of, say, 1).

More generally, numbers (signed short integers) can be used within A-code source interchangeably with symbolic constants. Compound symbolic constants, defined on the basis of numbers or previously defined constants, are also allowed for some special purposes.

Variables are symbolically named entities, each with a state and an instance of the variable flag set. There are some mandatory variables (e.g. HERE, see below), but any number of additional variables can be declared by the programmer. Words entered on the command line are available through the special mandatory variables ARG1 and ARG2. Yes, you've spotted it! This restricts A-code to simple verb/noun commands. Which makes A-code adventures more difficult to devise, but easier to play. Not that expanding the syntax to verb/object [/instrument] would be very difficult.

Texts are named or nameless entities representing a piece of text. Any amount of text can be associated with a text entity. Generally multi-line texts are considered to be unformatted, line wrapping and filling being done by the A-code kernel at run time. Texts can contain text "switches" which allow them to be context dependent. A text switch cannot be nested in another switch (that would be meaningless!), but otherwise can occur anywhere within the text, and having components of any length. Its format is [string0/string1/string2...] and must contain not less than two strings (possibly empty). When displayed, text can be qualified by the value of a constant, a variable, an object or a place. If the qualifying value is n and the text contains text switches, only string-n of every switch is displayed. If the qualifier value is higher than the number of strings in the switch, the last string of the switch is printed. This allows texts (and for that matter object and place descriptions, which too are texts), to be context dependent.

Another feature of A-code text is the processing of the # symbol. In a text qualified by a value, each occurence of the # will be replaced either by the numerical value in question or, depending on the directive used to display the text, if the qualifier is a verb or an object, by the primary word associated with the verb or object.

As noted, A-code kernel does its own text formatting and filling at run- time. However, if a text line has a forward slash as it first non-blank character, the rest of the line will be displayed verbatim as a separate line of output.

Finally, text nesting. While braces on directive lines indicate comments, within text they signal inclusion of another previously defined piece of text, which in itself may have further nesting. The syntax is {text_name}, where text_name is either the symbolic name of a piece of text, or a variable referring to text. (See below for the A-code indirection mechanism.)

Chunks of A-code are just that - named or unnamed chunks of A-code. They can be separate procedures or can be associated with verbs or places.

You may be wondering how does one refer to nameless entities of any sort? A-code groups together all entities of one class in the order of their declaration, allowing one to reference nameless ones by offsets from named entities of the same class. Essentially, you can view a sequence of nameless entities as an "array" associated with the last declared named one.

Places are symbolically named entities, each with a state, an instance of the place flag set, up to three different unnamed texts (brief, long and detailed descriptions) and a "hook" for unnamed chunks of A-code to be associated with the place. By default symbolic place names are not available in the player vocabulary, but each such name can be specifically forced into the vocabulary by prefixing the name in the place declaration with a +. Any number of synonyms can be defined for a place name, though of course, there is no point doing this unless such names are forced into the player vocabulary. All properties of texts (described above) apply to place descriptions.

Objects are also individually named (with any number of synonymous names) and once again, have to their name a state and an instance of the object flag set each, plus up to three different descriptions (inventory, long and detailed), but no A-code. Object descriptions also obey the rules common to any kind of text. Object names (and synonyms) do get entered into the player vocabulary by default, but it is possible to exclude any specific object name by prefixing the object name in its declaration with a -.

Verbs have symbolic names (with synonyms) and each is associated with one or more chunks of A-code. By default, verb names are added to the vocabulary ,but again, any specific name can be excluded. It is sometimes useful to have "dummy" verbs for internal house-keeping, but not available to the player. This is again achieved by prefixing the verb name with a -, in the verb declaration.

There is no restriction on the length of symbolic names, but only the first 16 characters are significant. Names may include any "reasonable" printing ASCII characters.

As noted above, some of the entity names are automatically or selectively added to the player vocabulary. To summarise: all verb names and their synonyms (except if otherwise indicated), all object names and their synonyms (unless otherwise indicated) and some place names and their synonyms (where specifically requested).

Vocabulary words can be abbreviated by player to the minimal unambiguous length. Nevertheless, If a word used by the player is echoed via a text qualified by either the ARG1 or ARG2 variable, the full matching dictionary word is displayed. It is also possible to enforce remapping of vocabulary words. E.g. it may be kind to allow the player to refer to an object by several different synonyms, yet always echo back only one of these. E.g. a matchbox could be referenced as matchbox or just box, but you may wish "get box" to result in "You get the matchbox.". To do this, use the declaration

OBJECT   MATCHBOX, =BOX

where the "equal" sign prefixing "BOX" is not a part of the synonym but specifies that if a player says "BOX", "MATCHBOX" should be assumed instead.

An automatic indirection occurs for some A-code directives when manipulating variables into which "addresses" of other entities have been loaded. Generally speaking this happens for non-arithmetical manipulation of variables.

Except within texts, casing is irrelevant - all keywords and opcodes are internally lowercased. The uppercasing of the A-code source is merely conventional.


A-code program structure

All declarations must precede any executable code. Forward referencing is not allowed. Within these restrictions, major (declarative) directives can occur in any order.

All major A-code directives (translator instructions and declarators) must start in column one. All other lines of A-code source must be offset from the beginning of the line by one or more tabs or spaces. The convention of starting all "other" lines in column 10 is not mandatory.

Directive, symbolic names and numerical values can be separated by commas and or spaces. The convention of using comma-space separators is not mandatory.


Major A-code directives

Major directives are either declarations or translator instructions. They must start in the first column of a line - that's how they are recognised in the first place, since everything else must be indented. The indentation depth is arbitrary - the convention of 9 leading spaces and subsequent indents of three spaces at a time is not mandatory.

There are three translator instructions:

The remaining major directives are all declarators. Within each declarative category (e.g. objects, places, texts) entities are stored sequentially in the order of declaration and hence can be referenced by numerical offsets from other entities.

Other than the dbname, flags, states and constants, all declared entities are associated with a "reference number" and are internally referenced by A-code only by this number. The reference numbers of entities of the same kind (e.g. objects, places, texts, variables...) are guaranteed to be lumped together into an unbroken numerical interval, with their reference numbers incrementing by one in the order in which the entities are declared. It is not necessary for the programmer to be aware of the reference numbers of declared entities, but it is helpful to be aware of this arrangement in general terms.

Reference numbers are used to achieve indirection in A-code. They can be loaded into variables, which are then recognised by most opcodes as "indirectors" so that the opcode is actually applied to the entity whose reference number the variable stores. This does not apply to arithmetical opcodes, making it possible to do "reference number arithmetic". This allows, for example, referencing nameless texts by offsets from other named texts, and has other uses too. It is not necessary to know the "reference number" of an entity, in order to load it into a variable.

Each of the above "major directives" must appear in column 1. A major directive embraces all following lines up to but not including the next major directive statement (i.e., all lines in which column 1 is blank). Any line with an asterisk in column 1 is considered to be a comment and is ignored. Comments may be placed on major-command and opcode-control lines (but not on text-string lines) by starting the comment portion of the line with a left-brace ('{') - the closing of a comment with a right brace ('}') is conventional but not mandatory.


Mandatory places, variables and flags

From MLA A-code version 10 onwards these are not so much "mandatory" as "reserved". If they are not defined, the acdc converter will define them automatically. It will only complain if your A-code source defines them to be of a wrong type.


Minor A-code directives (a.k.a. opcodes)

The actual A-code code consist of minor directives (or opcodes) followed by their arguments (if any). A line of code cannot have more than a single opcode line.

The following is a list of the available opcodes and a quickie description of what they do. As noted previously, in some cases if an argument is a variable, it is automatically de-referenced - i.e. the operation indicated by the opcode is applied not to the argument but to the entity referenced by the argument. Any such opcode arguments are denoted by a trailing asterisk in the following summaries.

First, tests with the "if" group terminated by a major opcode

The next lot are also tests, but their "if" groups must be terminated by the "FIN" opcode (or the deprecated "EOF" opcode).

Now for some logicals, to string the tests together, creating "compound" conditions. Note that tests are executed in the order in which they are encountered, with no precedence rules for operators and no bracketing.

Logically enough, here are delimiters for conditional code.

Next come the three iterative opcodes together with the iterative loop terminator.

Other execution flow control opcodes.

There are three directives for producing text output. All three can be "qualified" for the purposes of # substitution and/or the text switch mechanism. The VALUE qualifier is mandatory, but both SAY and QUIP can be used with a single argument. If the argument is an object or a place (possibly indirectly pointed to through a variable), the current state value of the object or place is used as the implied qualifier

These opcodes are used to alter values and flag settings.

Finally two opcodes for communication with the machine environment.

Oh yes, as an afterthought, perhaps something to communicate with the player.

That's it! Mind you, you won't understand A-code from this description alone. Go and read the A-code source.


The most common A-code error

Just a quick word on the most common error one makes in using A-code. It is dreadfully tempting to forget that ARG2 holds a pointer rather than a value and to write "IFEQ ARG2, value", by analogy with "IFEQ object, value". This is WRONG. The correct code would either refer to ARG2 by its object name (we often know by that time which particular object ARG2 points to) or use EVAL: "EVAL variable, ARG2" followed by "IFEQ variable, value".


Obsolete directives

These obsolete directives are supported for compatibility with old versions of A-code. They are honoured only in old-style A-code, i.e. when the A-code source does not start with a DBNAME directive. (Well, not strictly true, actually, but it's not safe to assume otherwise.)


Back to A-code page
Back to main page
Mike Arnautov, Thursday, 02-Oct-2008 04:24:52 MDT