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.