                Field Compiler for Step Lists


SUBCELLS

When a space is defined, the cell at each site in the space is, by
default, defined to be as large as the total memory will accommodate.
Thus in a machine with 2^31 bits of memory, if you define a 2D space
that has 512x512 cells, then each cell will contain 2^31/2^18 = 2^13
bits.  The first 16 bits of each cell are referred to as "subcell 0",
the next 16 are "subcell 1", and so on.

The lookup tables in CAM-8 process 16 bits of data at a time.  By
default, all operations involve only the bits of subcell 0.  The field
compiler allows you to run steps that involve bits of other subcells.
Any operation on any number of bits, up to the full size of a cell,
can be accomplished by using a sequence of suitably chosen 16-bit
operations on a sequence of 16-bit sets chosen from the full cell.  We
will call the group of 16 bits that are brought together for a given
16-bit update the "assembled subcell".


FIELDS

A set of consecutive cell bits, all belonging to the same subcell, can
be given a name by the user.  Such a set is called a "field".  For
example,

  0 subcell:

  0  7 == lo-byte               \ 8 bit field
  8 15 == hi-byte

  1 subcell:

  4  7 == rand0                 \ 4 bit field
  8  8 == boundary-flag         \ 1 bit field

The word "==" takes two arguments on the stack, and one name from the
input stream.  Thus in subcell 0, bits 0 through 7 are the "lo-byte"
field, and bits 8 through 15 are the "high-byte" field.

Field names are used for many purposes: during update-rule definition,
to specify data movement, for specifying I/O sources and destinations,
for display, and also to specify which 16 bits are to be gathered
together into the assembled subcell, and in what order, for the next
application of a rule table to all assembled subcells.


FIELD COMPILER

A set of Forth words are provided for use within step-lists in order
to gather together sets of fields.  These words worry about the
book-keeping involved in remembering where the fields came from, and
putting them back into their correct subcell before the next set of
fields are assembled.  The relevant words are:

  assemble-fields       (s cfa1 ... cfaN N -- )
  assume-field-order    (s cfa1 ... cfaN N -- )
  activate-subcell      (s n -- )
  activate-bit-fields   (s mask1 sub#1 mask2 sub#2 .. maskN sub#N N -- )
  activate-fields       (s acf1 acf2 .. acfN N -- )
  .active 
  let-fields-persist
  io-subcell            (s n -- )
  show-subcell          (s n -- )
  io-fields             (s cfa1 ... cfaN N -- )
  show-fields           (s cfa1 ... cfaN N -- )


Only the first 3 of these words are commonly used, and the primary
word used in step lists is "assemble-fields".


ASSEMBLE-FIELDS

A typical useage of "assemble-fields" might look like the following:

  define-step my-step

        { hi-byte rand0 }   assemble-fields
                
        site-src lut
        lut-src  site
        lut-data my-table   switch-luts

        kick hi-byte field   1 x  -1 y
             rand0   field  27 x  19 y
        run

  end-step


Remember that words that appear within a step list are executed, and
their effect is to add instructions to the current list of step
instructions being constructed.

The construct "{ field1 field2 }" puts a pointer to field1 onto the
stack, then a pointer to field2, and then the number "2" goes on the
stack, to show that there were only two fields specified.  The
"assemble-fields" word expects a list of such pointers, followed by an
integer specifying how many pointers are on the stack.  Given these
parameters, "assemble-fields" compiles a set of instructions into the
step list that bring the specified fields together in the order
indicated, with the least significant bit of field1 appearing at bit
position 0, and the least significant bit of field2 appearing
immediately after the last bit of field1.  Up to 16 fields can be
specified, in order to bring together up to 16 bits of data.  If fewer
than 16 bits are specified, then the unspecified high-order bits come
from subcell 0.

Note that the "kick" instruction uses the field names.  Although
"hi-byte" was orginally defined to be bits 8 though 15 of a subcell,
the name has been temporarily redefined within the step list by the
"assemble-fields" word.  It now refers to bits 0 though 7 of the
assembled subcell.  When "end-step" is encountered,
instructions are compiled into the step list to move all the fields
back to where they came from.  The temporary redefinition of which
bits a given field refers to is also undone.

If a single "define-step" specifies a sequence of assemble/kick/run
operations, then each "assemble-fields" operation undoes the assembly
performed by the preceding one before performing a new assembly; the
last assembly is undone by "end-step" (or by "step" in the case of a
unnamed temporary list that is constructed dynamically).


WHAT IS MOVED

If fields are used in the assembled subcell in the same position that
they were defined in, then only a pointer operation is involved in
CAM-8, and there is essentially no overhead incurred in the assembly.
If, as is the case in our example, the fields are used in different
places than they were defined, more work is required by CAM.  Extra
updating scans of the space will be constructed by "assemble-fields"
that permute the bits of the various subcells, so that all of the
fields are in the positions where they will be used.  Then they are
assembled by a no-overhead pointer operation.  Finally, at the
end-step, everything is put back, and CAM is always left in a state
where the assembled subcell consists of the 16 bits of subcell 0.

When a CAM experiment is loaded (compile time), the word
"assemble-fields" provides text feedback about what it is doing.  The
contents of each assembled subcell is listed in turn.  Each extra
permutation step inserted results in a line of feedback containing a
hexadecimal code describing the permutation.  If no such lines appear
during loading, then you know that all fields were used in the
relative positions within a subcell in which they were defined.  This
is the most efficient case, and when you have a working experiment,
you may decide to reorganize your definitions of where data is located
in subcells, in order to make your experiment run faster.

Note that if no permutations are needed, the word "assemble-fields"
guarantees that the lookup tables will not be altered.  Thus in this
case only, it is safe to load a lookup table needed by your step
*before* the "assemble-fields".  The advantage of this is that it
allows overlap of table loading and cell processing.  For example, if
the previous example had begun

  define-step my-step

        lut-data my-table   switch-luts
        { lo-byte boundary-flag }   assemble-fields
                ...
        run
  end-step

then this "assemble-fields" wouldn't involve any permutations, and so
can safely be placed after the "lut-data".  If the last instruction of
one step is a "run", and the first instruction of the next step
executed is "lut-data", then the lut will be downloaded while the
previous step runs.  This is possible in this case, because the
"assemble-fields" doesn't need to change the lookup tables in order to
do permutations, since none are required in this case.  If you use
this kind of optimization, you should watch the feedback during
loading to make sure that no permutations are generated.


ASSUME-FIELD-ORDER

The word "assume-field-order" takes exactly the same arguments as
"assemble-fields".  This word is used before table generation.  When a
rule is compiled into a lookup table, the field names are used within
a rule definition to pick out the appropriate bits of the assembled
subcell being evaluated, and put them on the stack.  For example,

  : my-rule  rand0 0 = if high-byte not -> high-byte then ;
  create-lut my-table

  { hi-byte rand0 } assume-field-order   ?rule>table my-rule my-table
  { cell }          assume-field-order

this rule looks at the bits of "rand0" and decides whether or not to
complement the bits of "high-byte".  Before it is compiled, we specify
the field order that we used in our first example: this table is
intended to be used by a step that specifies that particular field
order.  To restore everything to normal, we use the field name "cell",
which is a 16-bit field that points to subcell 0.


ACTIVATE-FIELDS

This word looks much like "assemble-fields", but it never generates
any permutations.  All fields indicated must be disjoint (not involve
any of the same bits within a subcell).  The specified bit fields from
various subcells are activated to form the assembled subcell.  The
order in which fields are listed is irrelevant, and any bit fields not
specified will come from subcell 0.  For example,

  { hi-byte rand0 }   activate-fields

would actually result in an assembled subcell consisting of bits 0
though 3 of subcell 0, bits 4 through 7 of subcell 1, and bits 8
though 15 of subcell 0.


ACTIVATE-SUBCELL

The word "activate-subcell" is used when all 16 bits of one particular
subcell should be activated.  Any bit fields previously assembled are
first put back, and then all of the bits of the subcell indicated on
the stack are assembled.  For example,

  29 activate-subcell

would prepare for updating the contents of subcell 29.  Note that you
don't have to have given names to any of the bits of subcell 29 in
order to activate it in this manner.


ACTIVATE-BIT-FIELDS

This word provides some low-level functionality for cases that would
otherwise be awkward.  For example, if we want to access the same bit
field within many different subcells, this provides a convenient
mechanism.   Like "activate-fields", no permutations are generated,
and the order of arguments is irrelevant.  Like "activate-subcell",
this word doesn't require that the fields specified have been given
names.  The argument list consists of alternating bit-masks and
subcell numbers, terminated by an integer indicating the number of
mask/sub# pairs.  For example ("h#" denotes a hexadecimal number),

  h# 100   2
  h#  ff  17   
  h# 600   1    3  activate-bit-fields

would get the first 8 bits from subcell 17, the next bit from subcell
2, the next 2 bits from subcell 1, and the final bit (which was
unspecified) would come from subcell 0.  We can use field names here
in the following manner:

                        h# 1  2
  high-byte field       layer-mask @  7
  rand0 field           layer-mask @  assembled-subcell# @
                        3  activate-bit-fields

The construct "fieldname field" sets the variables "layer-mask" and
"assembled-subcell#" to the current values associated with the given
field.  Thus this example assembles a subcell consisting of the first
bit of subcell 2, bits 1 through 3 of subcell 0, bits 4 through 7 of
subcell 1, and bits 8 through 15 of subcell 7.  This kind of construct
may seem strange, but if some of the subcell numbers are coming from
variables or loop indices, this kind of construct can be very useful.


.ACTIVE

For debugging purposes, the word ".active" can be used to display
which subcell bits are active at any give point in a program.



LET-FIELDS-PERSIST

If a step assembles fields, then it makes sure that you're left at
subcell 0 at the end.  You can override this guarantee using
"let-fields-persist" at the end (just before "step" or "end-step") but
then its your responsibility to be careful!  If the next step list is
a "define-step", then it will be compiled assuming that whichever
fields have persisted at compile time will also be present in CAM
whenever it starts executing.  If this assumption isn't true when the
step is later executed, fields will get put back in the wrong places,
and field pointers will be corrupted.  This construct can be used
safely before compiled steps that don't change the assembled subcell.
For example, the definition of *count* includes this word, so that you
can say things like

   1 activate-subcell  *count*  rand0 field count-field

which would add together all the "rand0"s from subcell 1 and put the
result on the stack.  After *count*, the assembled subcell has been
restored to subcell 0.


FIELD I/O AND DISPLAY

The word "io-subcell" directs subsequent high-level CAM I/O operations
(such as "file>cam" and "random>cam") to use the subcell specified on
the stack.  Similarly, "io-fields" redirects I/O, but it takes exactly
the same arguments as "assemble-fields".  It allows you to assemble
any desired subcell for I/O operations.  Similarly, "show-subcell" and
"show-fields" allow you to control which subcell or set of fields will
be displayed by subsequent "show" commands.  

Note that "show" ignores this global field specification if the
assembled subcell is not subcell 0 when it executes: in this case, it
displays the assembled subcell instead.

Note also that the "View-subcell" key-interpreter command calls both
"show-subcell" and "io-subcell".  Thus when you switch to viewing a
subcell, all I/O is also redirected to and from that subcell.

