TRACE Sequence Compiler

This page describes the sequence compiler syntax and is a reference for how to write a sequence. If you already have a sequence that you are ready to compile, you want the Prototype TRACE Sequence Compiler page.

This is a brief description of the proto-type sequence compiler. It is not complete yet and assumes that you are familar with Mike Levay's writeup. There are differences between the sequence language defined here and the earlier ideas in the Instrument Control Document. With the possible exception of errors, this is the correct description.

There are a few things to keep in mind when writing sequences. The only quantities that can change while a sequence is running are the ``registers'' used by the onboard meta code that runs the sequences. These registers are all 16 bits and most 16 bit arithemtic and logical operations are supported by the meta code instructions but not any 32 bit operations. Unlike the usual case when a program is compiled, there is no real access to memory outside these registers. Hence the concept of providing a user lots of named variables is not workable. There is no place to store their values other than the limited sets of registers. We can provide alias for the registers of course and we can provide convenient names for constants to make the code more readable. The alias for constants is already implemented but not for registers.

Constants are represented by names called symbols. These names are words that begin with a letter and generally look like symbols in Java or C. Symbols are used extensively for target, frame, and sequence names in the TRACE compiler. The values of these symbols are the 32 bit ID's for the targets, frames, and sequences. This causes some potential confusion because the arithmetic and registers are all 16 bit but some routines require the full 32 bit ID's (actually two 16 bit values) which we want to represent with a single symbol. As a general rule, if a symbol is used in an expression, only the lower 16 bits is actually used. Hence symbols uses as constants (otther than ID's) should be just 16 bits although the compiler will store 32 bits (in case it is needed). The upper bits are only used when the symbol is an argument for a target, frame, or sequence call, or for the special double register load statement (described below).

There are two types of registers, local and global, which are represented by $Lx and $Gx where x is a hexidecimal digit ranging from 0 to F for the locals and 0 to E for the globals. These can also just be called $Rx where now x covers all the possible registers with unique values for locals, globals, and an assortment that are used as flags and parameters. Locals are local to a particular sequence and when a new sequence is called it starts with a new set of locals. If a sequence calls another which then returns to the first sequence, the old locals return; i.e., they are stacked and then popped. The depth of this stacking will be revealed someday. Other registers are all global. The various mnemonics for the flags and parameters are:

$saa SAA flag, on when in predicted SAA
$hlf high latitude flag
$aaz atmospheric absorption zone
$fef flare flare
$mms mass memory status
$sef super flare flag
$tef transient event flag
$nbf number of available flare buffers
$cbf current flare buffer
$trc target class
$mxr row number of I max
$mxc column number of I max
$mxv value of I max
$mnr row number of I min
$mnc column number of I min
$mnv value of I min
$rtn return value
$pri priority
$pnd sequence pending flag
$rpt repeat, not actually used, available

Statements in the ``language'' include:

Other important parts are expressions and conditionals. Statements end with a semi-colon or a right brace (i.e, a ``}'') for groups of statements. More than one can be on a given line or a long one can span several lines. The syntax is similar to Java or C but much more limited in the types of expressions and conditionals. Before describing the details of the syntax, an example of code may help. This is part of a flare watch sequence. Note the use of C++ comments, regular C comments can also be used as in Java.


    $L0 = 90;           // 90 pre-flare images at most
    while ($L0--) {     // loop 90 times (or until a break)
    
    if ($nbf > 1)  frame(1400);	// stores to pre-flare buffer
    if ($nbf == 1) frame(1401);	// waits for a bigger flare
                       // because only one buffer
                       // left, otherwise similar
    if ($nbf == 0) frame(1402);	// still checks for flare
                       // but doesn't store at all
                       // because no buffers left
    if ($FEF) break;   // hey! a flare
    delay (400);       // every 20 seconds
    }

Assignments

Remember that only registers can be changed. Hence assignment statements are of the form:

    $Rx = expression;

where usually $R is a local or global register. Expressions are described below. The semi-colon at the end is required as in C. As with C, assignments without the semi-colon can also be expressions. This allows assignments to be used in conditionals. Also, chains of assignments are possible such as:


    $R1 = $R2 = $R3 = expression;

Double Register Load

This is a specialized statement used to conveniently load 2 registers with the 32 bit value from a frame or sequence ID symbol. The syntax is (currently):

    $Rx ~ symbol;

and causes $Rx to be loaded with the lower 16 bits and $Rx+1 (the next register) to be loaded with the top 16 bits of the 32 bit values represented by symbol. The "~" is perhaps not the optimal symbol for this operation and other suggestions are welcome. An expression, which is always 16 bits anyhow, it not legal here. Nor can this operation be chained.

Expressions

These are just Java-like, C-like, or Fortran-like combinations of registers and constants using arithmetic and logical operations and infix notation. Hence the following are expressions:


    $L3 + $G2
    $L3 + 10
    $L4 && $FEF
    $FEF || $TEF || $SEF
    12*($L2+$G3)*$G4 - ($L4/$L5)+$Ga
    $L9 > 14
    $G1 == 1
    $G1--

Legal operations are a subset of C and have the same relative precedents. They are, starting with highest precendent and listing operations with the same precedent on the same line:

unary operations


    -, !
++, --

binary operations


    *, /
+, -
>, >=, <, <=, ==, !=
&&
||

Note that only the logical and's and or's are supported and not the bitwise versions. All expressions are evaluated using 16 bit integer arithmetic. The increment and decrement operators (++ and --) cause one to be added or subtracted from a register; They can be used either before or after the register. When used after, the value of the expression is the register value before the operation. This is the same convention used in Java and C.

Subroutine Calls

These are calls to the built in subroutines. Each has a fixed number of arguments (none to three). Most look like C functions without a returned value; i.e., the format is:


    sub_name(arg1, arg2, arg3);

The semi-colon is required. For subroutines without arguments, the parenthesis are not required. All the known subroutine are listed as they would appear in a sequence below:

No arguments -


	srt;
	term;
	resetaec;
	lockbuf;
	startave;
	updimax;
	tap;
	openloop;
	closeloop;

One argument -


	wait(10);
	delay(10);
	tim(10);
	call(10);
	frame(10);
	target(10);

Two arguments -


	dev(10,10);
	samp(10,10);
	setdhcr(0x51, 0x1800);
	timchk($r54, $l2);
	lda($l0, 0x720c);

Three arguments -


	framepf(10,10,10);

While Loops

The format is:


    while (expression) statement;

or


    while (expression) { statement1; statement2; ... }

The expression enclosed in parenthesis is called the conditional. The parenthesis are required. The following statement is executed if the conditional evaluates to a non-zero value. Otherwise, the sequence skips over to the next statement after this construction. Remember that expressions can contain assignments. Also, a statement can be a list of statements enclosed in curly brackets. While's are used to construct loops or to wait for an event. Some examples are:


    /* note that this will execute the frames only 9 times */
    $L0 = 10;   while ($L0 = $L0 - 1) { frame(1400);  frame(1401); }

    /* while this will do the loop 10 times */
    $L0 = 10;   while ($L0) { frame(1400);  frame(1401); $L0 = $L0 - 1; }

    /* this is another way to do the loop 10 times*/
    $L0 = 10;   while ($L0--) { frame(1400);  frame(1401); }

    /* wait for a flare (until 2001?) */
    while ($fef != 1) { }

if/else

If statements are supported with an optional else section. The formats are:


    if (expression) statement

and


    if (expression) statement else statement

These can be nested. The comment above in the while section regarding expressions and statements also applies here. For TRACE sequences, if statements can be used to test flare flags and buffer statuses, etc. They can be used with ``breaks'' (described below) to break out of a while loop.

breaks

Execution of a break statement cause the program to jump just beyond the current while loop. A break executed outside of a while loop is a no-op. If program has nested while loops, escape is only from the current one and any other while loops continue. A break is the only way to escape an infinite while loop. A break will usually be used in an if statement which checks for a flag, etc. Some examples are:


    /* take pictures using frame 1400 until a flare occurs */
    while (1 == 1) { frame(1400);  if ($fef) break; }

    /* take 100 pictures but interrupt for a transient */
    $L0 = 100; while ($L0--) { frame(1400);  if ($tef) break; }

goto's

Like C but unlike Java, there is a goto statement. Any statement can be labled as desired with a symbol followed by a colon. Label symbols must begin with a letter or underscore followed by any letter or number or dots (periods). The rule is the same as for symbols used in expressions. A goto can be used to jump to any labeled statement. Excessive use of goto's is generally discouraged since it often results in undecipherable code.

Functions

Some of the internal functions that are called from sequences are described below. This list is not yet complete. The general syntax is like a JAVA or C function call (see examples above).

DELAY(ticks)

The DELAY function delays sequence execution for the specified number of ticks since the last delay. Other instructions may have executed in the meantime so DELAY is useful for cadence control (provided the delay is longer that the time it takes to execute the other instructions). You can prime DELAY with a DELAY(0). This will just zero the internal counter. The internal counter is incremented every 0.05 seconds but is not checked until another DELAY is executed. At that time, the counter is checked to see if it is greater than the DELAY argument. If so, execution resumes (i.e., the time has already passed); if not, the control computer waits until the desired time has elapsed before any other execution is performed. Hence, if images are desired on exactly a 2.5 minute cadence, the following code fragment will do it -


	/* do 100 images spaced 2.5 minutes apart */
	$L0 = 100;
	delay(0);	// prime delay, zero counter
	while ($L0--) { frame(my_picture);  delay(3000); }

The counter that DELAY uses is stacked during sequence calls and restored after the sequence returns. This means that each sequence in a nested set can have a unique delay counter. However, it is very important to understand that the counter does NOT get incremented while stacked, it only counts time spent in its own sequence and does not count time spent in a sub-sequence that may be called. Hence, using DELAY when some of the instructions are calls to other sequences may not make sense in general. In particular, REMEMBER that execute_list is a sequence that gets called like any other and stacks the delay counter. When a sequence is called from another, it inherits the value of the counter from its parent. This means that the first DELAY in a sequence will reference the time since the last DELAY in the parent. If a DELAY(0) is used to prime the counter, there is no real effect. In principle, this inheritance could be used for some timing applications but it may be difficult to understand such code (implies bugs and mysterious behavior). For timing across sequence calls, the SRT/TIM instructions are provided.

WAIT(ticks)

The WAIT function unconditionally delays sequence execution for the specified number of ticks. It does this by setting a dedicated counter and does not execute any other sequence instructions until it counts down. It is not used for maintaining cadence since it cannot count time while other instructions are executing. See DELAY for that purpose.