Contents

Previous

Next

Last

Sample Scripts

This section includes some sample scripts that illustrate some of the things that can be done with the Control Manager and CMS and the techniques that are used. It's by no means exhaustive, such a thing is probably not even possible, but it will hopefully give you some ideas for creating your own CMS functions.

Some of samples in this section have rather long lines due to the comments. For maximum readability, you will probably need to maximize the Help Window to prevent the lines from "wrapping" to the next line and making things appear a bit confusing.

Example #1 - Combining Axes

This sample shows how you can use the CMS to combine axes to form new axes that are more useful in a particular situation. In this case the problem will be to combine the toe brakes into a single Y-Axis. This function is sometimes useful in racing simulators that don't provide for independent gas and brake pedals, but rely on the pedals going one way for gas, the other for brake, with the center position essentially being "no gas or brake".

Before starting the script, we need to look at the problem, consider the facts, and then figure out how we're going to approach it.

The first useful fact here is that all axes return 0 to 255 for full travel. This is true of the axes that the real controllers are generating as well as the values that need to be sent to Windows via the CMS Controls.

The second thing we need to know is that the Left Toe Brake is the A1 axis for the ProPedals and the Right Toe Brake is the A2 axis. It's easy to see the values and in which direction they go using the Test/Calibrate screen, and the GUI will give you the name of the device if something other than a simple button.

Finally we need to know what the simulation wants. Normally is a 0 for full throttle, a 255 for full brakes, and a 128 for neither, but this can vary.

When it's all boiled down to the bare essentials, we have two axes, the toe brakes, both of which are sending values of 0 when they're released and 255 when they're completely pressed. The idea is to generate one axis that goes from 128 (the effective center value) to 0 as one pedal is pressed, and goes from 128 to 255 when the second pedal is pressed. It's just simple arithmetic really, so simple arithmetic is what we'll do.

The Script

For this sample, we'll assume that there is a Yoke that we'll use as a steering wheel on the first Device Tab in the GUI, it would be JS1. We'll also assume that the ProPedals are on the second Device Tab in the GUI and are thus JS2. If your devices are on a different Device Tabs, then these references would need to be changed. The script would look like this:

SCRIPT

  CMS.A1 = 128 + (JS2.A2/2) - (JS2.A1/2);

ENDSCRIPT

It's really just one line of script. It starts with the center value of 128, then it adds one half the Y-Axis value and subtracts one half the X-axis value. Thus, pressing the Right Toe Brake (A2) increases the initial 128 value towards 255, while pressing the Left Pedal (A1) reduces the initial value towards 0, which is just what we wanted.

To complete the map, the axis assignments have to be made out in the GUI. The Yoke X-Axis would be assigned to the X-Axis on CM Device 1. The Yoke Y-Axis would be assigned to "None" since it won't be used. Axis 1 on the CMS Controls would be assigned to the Y-Axis on CM Device 1.

Example #2 - Map Mode Control

As was mentioned in the section on setting the Mode, it is possible to use the CMS to control which Mode the map is using via the CURRENTMODE variable. In this example, we'll implement a cycling mode switch that operates much like the hardware mode switches on the FighterStick and ProThrottle.

The only real fact of interest here is that, to cycle through the three Modes, we need to set the CURRENTMODE variable to 0, then 1, then 2, then back to 0. In this example, we use one of the internal Analog Variables, A1, to keep track of which mode we're going to set. That variable will be incremented by one every time a button is pressed, and when it goes above 2, we'll reset it to zero. A sequence is probably the simplest way to implement this since we can use the WAIT() function to stall the sequence until our button is clicked.

The Script

For this map, we'll assume that there's just a CombatStick in the map and we want the Mode button to be Button 2 on that device. The script would look something like this:

SCRIPT

  SEQUENCE
    WAIT( JS1.B2 );          // Wait here until JS1.B2 clicks
    A1 = A1 + 1;             // Add 1 to our Mode counter
    IF( [ A1 > 3 ] ) THEN    // If the value is over 3 then
      A1 = 0;                // set it back to 0
    ENDIF
    CURRENTMODE = A1;        // Now copy it to CURRENTMODE
  ENDSEQUENCE

ENDSCRIPT

JS1.B2 will increment A1 by 1 every time it's clicked. When A1 goes above three, it gets reset to zero. The values will cycle 0, 1, 2, 3, 0, 1, 2, 3, etc. thus cycling through the four modes, just as we intended. Since this doesn't control any CM Device axes or buttons, there is really nothing that needs to be assigned in the GUI except that Button 2 on the CombatStick should be assigned to "None" so it doesn't interfere with whatever else the map is doing.

Another method would be to control the four modes with a single 4-way hat. This has the advantage that all four modes are available instantly without having to cycle through the other 3 modes, and we can tell which Mode we are in by the direction that we push the hat.

For this example we'll assume we have a FighterStick and wish to use Hat 3 to control the four Modes. Hat 3 generates Buttons 13 through 16. The script itself is straightforward, just a series of nested IF/THEN/ELSE blocks that set the CURRENTMODE variable based on which of the current position of Hat 3. It's useful to remember when using hats that they can only have one position active at a time so we don't need to make any allowance for a situation where B13 and B14 were closed at the same time.

The script might look like this:

SCRIPT

  IF( JS1.B13 ) THEN
    CURRENTMODE = MODE1;
  ELSE
    IF( JS1.B14 ) THEN
      CURRENTMODE = MODE2;
    ELSE
      IF( JS1.B15 ) THEN
        CURRENTMODE = MODE3;
      ELSE
        IF( JS1.B16 ) THEN
          CURRENTMODE = MODE4;
        ENDIF
      ENDIF
    ENDIF
  ENDIF

ENDSCRIPT

Out in the GUI, the Hat 3 positions should, of course, be programmed to "NONE" so as not to send any commands directly.

Example #3 - Automatic Trim

This example implements an automatic trim function. In practice, you move the joystick until you're flying straight and level. At that point, you press a button which locks the axes in that position. With the button held down, you return the joystick to its center position and release the button. The trim setting will hold the controls in the trimmed position when the joystick is centered, and the stick will move the controls from that position as the joystick is moved from center. To reset the trim, you leave the joystick centered and click the button again.

Again, this is just arithmetic. We need to find out what the values are at the trim position, then use those to calculate the difference between the values there and the values for joystick center. Once we have these offsets, we just add them to the actual joystick values to produce the "trimmed" values.

The Script

For this example, we'll assume that there's a single CombatStick in the map, it will be JS1, and that we want to use Button 2 on the CombatStick as our "Trim" button. The script is really a two-step process. First, calculating the Trim values when the Button 2 is pressed, then applying the trim values when Button 2 is released. We'll use the internal variables A1 and A2 to keep track of our trim offset for the X and Y axes respectively.

The offsetting of the joystick values must go on continuously while the button is released so that every reading gets updated. On the other hand, the calculation of the offset values only needs to be done when the button is first clicked. An IF/ENDIF block is most appropriate for the former, a SEQUENCE for the latter.

The script would look like this:

SCRIPT

  IF( NOT JS1.B2 ) THEN     // If Button 2 is released then
    CMS.A1 = JS1.A1 + A1;   // add the X offset to the stick X value
    CMS.A2 = JS1.A2 + A2;   // and add the Y offset to the stick Y value
  ENDIF

  SEQUENCE
    WAIT( JS1.B2 );         // Wait until Button 2 clicks
    A1 = JS1.A1 - 128;      // Calculate the X offset and save it in A1
    A2 = JS1.A2 - 128;      // Calculate the Y offset and save it in A2
  ENDSEQUENCE

ENDSCRIPT

In the GUI, some assignments need to be made. First, the X and Y axes for the CombatStick need to be assigned to "None" so they don't interfere. Second, the CMS.A1 and CMS.A2 values need to be assigned to the X and Y axes on CM Device 1 so the trimmed values are available to Windows.

Example #4 - Using the %DEFINE Statement

This example illustrates some of the things that can be done using the %DEFINE directive. This provides essentially the same function as the AutoTrim example above, but makes rather heavy use of the %DEFINE directives.

The Script

The script actually generates exactly the same thing as the AutoTrim Example. The main difference is in the %DEFINE statements at the top of the script and then the way that things are referenced later by name. It looks like this:

// Named variables to hold the trim offsets
//
%DEFINE xTrimValue       A1
%DEFINE yTrimValue       A2

// Names for the actual joystick axes
//
%DEFINE xStickValue      JS1.A1
%DEFINE yStickValue      JS1.A2

// Friendly names for GUI axes that go to Windows
//
%DEFINE xCmsValue        CMS.A1
%DEFINE yCmsValue        CMS.A2

// Name a constant for the axis center value
//
%DEFINE centerStickValue 128

// Name a couple of bit functions.
//
%DEFINE trimButtonPressed     JS1.B2
%DEFINE trimButtonReleased    NOT JS1.B2

// The definitions are complete. Now we write the script.
//
SCRIPT

  // This trims the joystick values and puts them in the CM Device
  // Axes while the trim button is released. This is the normal run
  // situation.
  //
  IF( trimButtonReleased ) THEN
    xCmsValue = xStickValue + xTrimValue
    yCmsValue = yStickValue + yTrimValue
  ENDIF

  // This waits for the trim button to be pressed, then records
  // how far from stick center that the X and Y values are. Those values
  // are used as the trim correction that will be in effect when the
  // trim button is released.
  //
  SEQUENCE
    WAIT( trimButtonPressed );
    xTrimValue = xStickValue - stickCenterValue;
    yTrimValue = yStickValue - stickCenterValue;
  ENDSEQUENCE

ENDSCRIPT

The above example is perhaps "overkill", but shows many of the things that can be done. When you use the %DEFINE statements, keep in mind that the idea is to help make clearer what the functions and variables actually do. It's entirely possible to "go too far", collapsing things to the point that the script becomes difficult to follow. For example, you could easily define something like this:

%DEFINE setXTrim    A1 = JS1.A1 - 128
%DEFINE setYTrim    A2 = JS1.A2 - 128

 SEQUENCE
   WAIT( trimButtonPressed );  // Wait for the button press
   setXTrim;  // Set the X trim value
   setYTrim;  // Set the Y trim value
 ENDSEQUENCE

The script that would be generated is exactly the same, but how the program sets the X and Y trim values gets hidden and rather difficult to understand.

Example #5 - Sending Multiple Character Strings

There are times when it is desirable to send several strings based on the press of only a single button. Each press sends the next string in the series. This can't be accomplished in the GUI, but it's certainly possible using the CMS. The approach taken in this example is to simply use several of the buttons from the CMS Controls and program each to send a different string using the GUI. The script takes care of cycling through the strings when the correct button is pressed.

The Script

There are a couple of ways to do this. The simplest is probably just to use a seqeuence. We'll again assume there's a single CombatStick in the map as the JS1 device and we want to use Button 2 on that to cycle through the messages. We'll also assume that there are four messages and that we want to trigger them with CMS.B1 through CMS.B4. It would look something like this:

SCRIPT

  SEQUENCE
    WAIT(JS1.B2);    // Wait the first click
    CMS.B1 = TRUE;   // Press CMS Controls Button 1
    WAIT(JS1.B2);    // Wait for the next click
    CMS.B1 = FALSE;  // Release CMS Control Button 1
    CMS.B2 = TRUE;   // Press CMS Controls Button 2
    WAIT(JS1.B2);    // Wait for the next click
    CMS.B2 = FALSE;  // Release CMS Control Button 2
    CMS.B3 = TRUE;   // Press CMS Controls Button 3
    WAIT(JS1.B2);    // Wait for the next click
    CMS.B3 = FALSE;  // Release CMS Control Button 3
    CMS.B4 = TRUE;   // Press CMS Controls Button 4
    WAIT(JS1.B2);    // Wait for the next click
    CMS.B4 = FALSE;  // Release CMS Control Button 4
  ENDSEQUENCE

ENDSCRIPT

Another method could make use of the SELECT function to accomplish the same thing. The SELECT will be controlled by one of the internal Analog Variables, its value will be controlled by JS1.B2. First we wait for the click, then set the new value. That gets processed through the SELECT statement, each case turning on one of the CMS.Bx outputs and triggering the string. It might look like this:

SCRIPT

  SEQUENCE
    WAIT( JS1.B2 );           // Wait until Button 2 is pressed
    A1 = A1 + 1;              // Increment our message counter
    IF( [ A1 > 3 ] ) THEN     // If A1 is over three then
      A1 = 0;                 // Reset A1 to 0
    ENDIF
  ENDSEQUENCE

  SELECT( A1, POSITION ) OF

    CASE 0:
      CMS.B1 = TRUE;       // Set the bit for our first message
    EXITCASE:
      CMS.B1 = FALSE;      // Clear the bit for our first message
      BREAK;

    CASE 1:
      CMS.B2 = TRUE;       // Set the bit for our second message
    EXITCASE:
      CMS.B2 = FALSE;      // Clear the bit for our first message
      BREAK;

    CASE 2:
      CMS.B3 = TRUE;       // Set the bit for our third message
    EXITCASE:
      CMS.B3 = FALSE;      // Clear the bit for our third message
      BREAK;

    CASE 3:
      CMS.B4 = TRUE;       // Set the bit for our fourth message
    EXITCASE:
      CMS.B4 = FALSE;      // Clear the bit for our fourth message
      BREAK;

  ENDSELECT

ENDSCRIPT

In the GUI we would need to make the necessary Programmed Mode assignments to CMS.B1 through CMS.B4 to define each of the four strings that we want to send.

Example #7 - Analog Trim Function

An analog trim function for axes as the elevator can be a useful thing to have as it provides better control then the common Trim+ and Trim- key commands. Some flight simulations provide such a function and they can simply be assigned to an axis and used directly. If not, then the CMS can be used to generate one. Again, it's just arithmetic. We need to take an unused axis and use its value as an offset to the main control value for the axis we want to trim.

The Script

For this example, we'll assume that we have a FighterStick on the first Device Tab (JS1) and a ProThrottle on the second device tab (JS2). Since we're using the ProThrottle to control the throttle itself, we can use the throttle wheel (A3) on the FighterStick as our trim axis. We want to trim the elevator (A2). We'll limit the available trim to 10% of full travel. We'll use CMS.A1 as the value that provides the final elevator setting.

Again, it's just arithmetic. We need to offset the trim value so it goes positive and negative, providing a 0 at the center position. Since it's a real axis (JS1.A3) we simply subtract 128 from its actual value to get a value between -128 and +127. We then divide that by 10 to give us the 10% trim limit, and add it to the value for JS1.A2. Finally, we check to see if the result has gone out of range and set limits on it if it has.

The script would look something like this:

SCRIPT

  CMS.A1 = JS1.A2 + (( JS1.A3 - 128) / 10); // Calculate the trimmed value

  IF( [ CMS.A1 > 255 ] ) THEN  // If the result is too high then
    CMS.A1 = 255;              // set it to the maximum allowable
  ENDIF

  IF( [ CMS.A1 < 0 ] ) THEN    // If the result is too low then
    CMS.A1 = 0;                // set it to the minimum allowable
  ENDIF

ENDSCRIPT

In the GUI, we would need to assign Axis 1 of the CMS Controls to control the Y-Axis on CM Device 1. The Y-Axis on the FighterStick would be assigned to "None" to disable it.

Example #8 - Switching Between Trackball and Ministick

If you're using a Trackball in your map, you probably use it to control the mouse most of the time. There are times, though, when it might be desirable to switch to the ministick on the ProThrottle or some other set of joystick axes to control the mouse. This isn't possible using just the GUI since you cannot make two assignments to the mouse DX device.

It can be accomplished using the CMS scripting, though. This example shows one possible method. It also shows how the XRELATIVE, YRELATIVE, and ZRELATIVE flags are uses.

The Script

For this example, we'll assume that we've got a FighterStick as Joystick 1, and ProThrottle as Joystick 2, and a TrackballPro as Joystick 3. We need a way to determine whether to take the data from the Trackball or from the Ministick. We can do this by just checking if the Ministick is off-center. If it is, we'll assume that we should take the Ministick data for the mouse move. If the Ministick is centered, we'll just take the TrackballPro data directly. That way, the switch should be more or less invisible. We'll also need to switch the ?RELATIVE flags at the same time. We handle the two axes more or less independently to allow the axes to respond independently. The script might look like this:

SCRIPT

  // Set the ?RELATIVE flags if the Ministick axes are centered.
  // This then disables the trackball when the Ministick is off center.
  //
  XRELATIVE = (JS2.A1 > 120) AND (JS2.A1 < 136));
  YRELATIVE = (JS2.B1 > 120) AND (JS2.B1 < 136));

  // If X is centered, it's relative and we use the Trackball.
  // Otherwise, we use the Ministick.
  //
  IF( XRELATIVE ) THEN
    CMS.A1 = JS3.A3;
  ELSE
    CMS.A1 = JS2.A1;
  ENDIF

  // Now we do the same with Y.
  //
  IF( YRELATIVE ) THEN
    CMS.A2 = JS3.A4;
  ELSE
    CMS.A2 = JS2.A2;
  ENDIF

ENDSCRIPT

In the GUI, we would program CMS.A1 and CMS.A2 to control the Mouse X and Mouse Y axes respectively and we'd be finished.

Example #9 - Switching Between Controllers

There are occasions when it might be desirable to use multiple sets of controllers and be able to select which set is active. Such a situation might arise, for example, if we wanted to set up a dual-control cockpit for an instructor/student or pilot/copilot operation. We need some way to set which set of controls is going to be the active set. This is easily done using CMS.

The Script

For this script we'll assume that we have two Yoke LEs and two sets of Pro Pedals. In the map, they're set up with the pilots Yoke LE on the first tab (JS1), the pilots ProPedals on the second tab (JS2), the copilots Yoke LE on the third tab (JS3), and the copilots ProPedals on the fourth tab (JS4). We'll also assume that Button 1 on the pilots Yoke LE is to be used to toggle between one set of controls and the other.

The script is fairly straightforward. We set things up so that the internal bit variable B1 is in control of which set of controls is in use, then we set up a short sequence to toggle B1 every time that JS1.B1 is clicked. Then, using an IF/THEN/ELSE block based on the state of B1, we assign one set of controls or the other to a set of CMS axes. We'll only do the X, Y, Throttle, and Rudder axes for now to show the method. A practical example would probably need to do a similar thing with the buttons and toe brakes. Also, since B1 is initially false, the pilots position is indicated by B1 being FALSE and the copilots position is indicated by B1 being TRUE.

The script would look like this:



SCRIPT

  SEQUENCE
    WAIT( JS1.B1 );    // Wait until the pilots Button 1 is pressed.
    B1 = NOT B1;       // Flip the state of the B1 variable
  ENDSEQUENCE

  IF( B1 ) THEN        // If b1 is TRUE then we're doing the copilot so
    CMS.A1 = JS3.A1;   // copy the copilots X axis,
    CMS.A2 = JS3.A2;   // Y axis,
    CMS.A3 = JS3.A3;   // Throttle,
    CMS.A4 = JS4.A3;   // and Rudder Axis to the CMS axes.
  ELSE                 // Otherwise we're doing the pilot so
    CMS.A1 = JS1.A1;   // copy the pilots X axis,
    CMS.A2 = JS1.A2;   // Y axis,
    CMS.A3 = JS1.A3;   // Throttle,
    CMS.A4 = JS2.A3;   // and Rudder Axis to the CMS axes.
  ENDIF

ENDSCRIPT

In the GUI, we'd assign the CMS Controls to CM Device 1. CMS Axis 1 would go to the X-Axis, CMS Axis 2 would go to the Y-Axis, CMS Axis 3 would go to the Z-Axis, and CMS Axis 4 would go to the R-Axis. We'd also need to unassign the X, Y, Z, and R axes from the yokes and pedals to prevent conflicts.

Example #10 - Changing Joystick Response

In some circumstances, it can be desirable to change the response of your Controllers while you're using them in a game or simulation. For example, in a combat flight simulation, you might like to use a relatively insensitive response for normal flying, then switch to a more sensitive response during a dogfight to be able to respond more quickly. This example shows how you might set this sort of thing up using the SCALE function.

The Script

The script for doing this is relatively simple. We need a "toggle" that indicates which of the two sets of response parameter are active. The state of this toggle is then used in an IF/THEN/ELSE block to activate the proper response. In this example, we're doing it inside a sequence. This is more efficient since the last SCALE function remains in effect until another SCALE function is executed. Using the sequence means that the scaling code will only execute when the button is clicked.

For the script, we assume that there's a FighterStick on the first Device Tab, so it's JS1 that we're dealing with. We're going to use Button 4 as the response selection button. If our toggle is TRUE, we select a less sensitive set of parameters, if the toggle is FALSE, we select the more sensitive set. Note that before the button is clicked for the first time, whatever parameters were set in the GUI will be in effect, and that the first click will set the less sensitive parameters since B1 is initial FALSE. The script would look like this:

SCRIPT

  SEQUENCE
    WAIT( JS1.B4 );                    // Wait for Button 4
    B1 = NOT B1;                       // Flip the toggle
    IF( B1 ) THEN                      // If the toggle is TRUE then set
      SCALE( JS1.A1, 50, 5, GAIN3 );   // Sensitivity=50%, Deadzone=5%
      SCALE( JS1.A2, 50, 5, GAIN3 );   // and Gain Curve=3
    ELSE                               // Otherwise set
      SCALE( JS1.A1, 100, 5, GAIN6 );  // Sensitivity=100%, Deadzone=5%
      SCALE( JS1.A2, 100, 5, GAIN6 );  // and Gain Curve=6
    ENDIF
  ENDSEQUENCE

ENDSCRIPT

In the GUI, we really wouldn't need to do anything at all. The normal JS1 assignments to X and Y are the ones that the SCALE function controls, so we're just acting on the real controls and there are no CMS assignments necessary.

Example #11 - Generating Repeating Sequences

Sometimes you might need to generate a repeating sequence of events for some period of time. An example might be the release of Chaff and Flares in a jet fighter simulations. The SEQUENCE statement is the obvious choice for this, and it ends up being very much like Example #5, the main difference being that a button would be used more to enable the function while DELAY instructions would be used to step through the sequence.

The Script

The script is straight forward. We'll assume that we have a FighterStick and that we want to use Button 2 to enable the sequence. Further, we need two commands, one for Chaff and one for Flares. We want them to alternate so long as Button 2 is held down with a 5 second spacing between the commands.

Since we want the action to continue so long as Button 2 is held, we'll use the WHILE() function to control that. We'll also use CMS.B1 to send the "Chaff" command and CMS.B2 to send the "Flares" command. The script would look like this:

SCRIPT

  SEQUENCE
    WHILE(JS1.B2);   // Keep it going so long as the button is down
    CMS.B1 = TRUE;   // Press CMS Controls Button 1 for Chaff
    DELAY( 100 );    // Wait for about 5 seconds
    CMS.B1 = FALSE;  // Release CMS Controls Button 1
    CMS.B2 = TRUE;   // Press CMS Controls Button 2 for Flares
    DELAY( 100 );    // Wait for another 5 seconds
    CMS.B2 = FALSE;  // Release CMS Controls Button 2
  ENDSEQUENCE

ENDSCRIPT

Out in the GUI, we'd need to make the assignment on CMS Controls Button 1 and Button 2 for Chaff and Flares. These should be programmed with the leading NULL character to keep them from sending repeating characters for the full 5 seconds each time.

Example #12 - Using SELECT Blocks

This is not really a practical example, more a demonstration of some of the advanced functions that can be implemented using the SELECT function. This function can be used for a number of fairly complex operation beyond just responding to the current state of an axis. This example will illustrate a few of the possibilities.

One of the more interesting is the ability to send or control one group of operations when an analog axis is moving one way, another group if the axis is moving in the opposite direction. While the Up/Down programming available using the GUI can do this for single characters, controlling more elaborate functions can be tricky.

The key to doing this is to track the current position using an analog variable. Then, when we enter a new "case", we can check that variable to tell you what the last case was and thus know whether the axis is increasing or decreasing. The basic structure would need to look like this:

SCRIPT

  SELECT( JS1.A3, RANGE ) OF
    CASE 0:
      A2 = 0;  // Set State 0, we must have come from Case 64
      BREAK;

    CASE 64:
      IF( [ A2 EQ 0 ] ) THEN
        // We came from Case 0
      ELSE
        // We must have come from Case 128
      ENDIF
      A2 = 1;   // Set State 1
      BREAK;

    CASE 128:
      IF( [ A2 EQ 1 ] ) THEN
        // We came from Case 64
      ELSE
        // We must have come from Case 192
      ENDIF
      A2 = 2;   // Set State 2
      BREAK;

    CASE 192:
      A2 = 3;   // Set State 3, we must have come from Case 128
      BREAK;

  ENDSELECT

ENDSCRIPT

Each case in the above checks the analog variable A2 to determine where it came from. It then registers its own state id in analog variable A2 so that the next state can tell where it came from.

Another possibility is that we might want to control some group of sequences based on the SELECT block. We would need to control them externally using internal flags that were set in the "CASE" part of each case, and then cleared in the "EXITCASE" part.

That introduces a potential problem in that we probably don't want one case to start until the sequence initiated by the previous case has been completed. This, too, is not too difficult to guarantee. We define a flag that controls when the SELECT block actually sees the value change. Each sequence can then set this flag to lock out any further changes in the SELECT value until its corresponding sequence has completed.

A script to implement this logic might look something like this:

SCRIPT

  // This is the lockout logic. If B1 is FALSE, then we copy JS1.A3
  // to the SELECT value. If B1 is TRUE, then we skip this, the
  // SELECT value does not change, and so the SELECT block stays in
  // whatever state it's currently in.
  //
  IF( NOT B1 ) THEN  // If B1 is FALSE, then we copy the
    A1 = JS1.A3;    // real value to our SELECT value.
  ENDIF;

  SELECT( A1, RANGE ) OF
    CASE 0:
      B1 = TRUE;    // Lock out the value change in the SELECT block
      B2 = TRUE;    // B2 will control our first sequence
    EXITCASE:
      B2 = FALSE;   // Clear our first sequence flag
      BREAK;

    CASE 64:
      B1 = TRUE;    // Lock out the value change in the SELECT block
      B3 = TRUE;    // B3 will control our second sequence
    EXITCASE:
      B3 = FALSE;   // Clear our second sequence flag
      BREAK;

    CASE 128:
      B1 = TRUE;    // Lock out the value change in the SELECT block
      B4 = TRUE;    // B4 will control our third sequence
    EXITCASE:
      B4 = FALSE;   // Clear our third sequence flag
      BREAK;

    CASE 192:
      B1 = TRUE;    // Lock out the value change in the SELECT block
      B5 = TRUE;    // B5 will control our fourth sequence
    EXITCASE:
      B5 = FALSE;   // Clear our fourth sequence flag
      BREAK;

  ENDSELECT

  // Now we can do the four sequences. Each one will clear the
  // the B1 flag when it's done so that the SELECT block can
  // update the SELECT value.
  //
  // These sequence just have a wait for their corresponding bit.
  // Then they execute a DELAY and clear the lock on the SELECT
  // block data transfer. They don't really do anything practical.
  //
  SEQUENCE
    WAIT( B2 );    // Wait for our first sequence flag
    DELAY( 10 );   // Some useful function would go here
    B1 = FALSE;    // Disable the lockout so the SELECT can move
  ENDSEQUENCE

  SEQUENCE
    WAIT( B3 );    // Wait for our second sequence flag
    DELAY( 10 );   // Some useful function would go here
    B1 = FALSE;    // Disable the lockout so the SELECT can move
  ENDSEQUENCE

  SEQUENCE
    WAIT( B4 );    // Wait for our third sequence flag
    DELAY( 10 );   // Some useful function would go here
    B1 = FALSE;    // Disable the lockout so the SELECT can move
  ENDSEQUENCE

  SEQUENCE
    WAIT( B5 );    // Wait for our fourth sequence flag
    DELAY( 10 );   // Some useful function would go here
    B1 = FALSE;    // Disable the lockout so the SELECT can move
  ENDSEQUENCE

ENDSCRIPT

There is another interesting possibility with the above sort of logic. Rather than basing the input to the SELECT block on one of the real axes, we could simply base it on a value that was set in each of the SEQUENCEs. In that case, the ordering of the various states could depend completely on, for example, what the value of CURRENTMODE is, causing the SELECT to transition from one state to any of three other states depending on the current Map Mode. Any set of conditions, really, can be used to determine what the next state might be. All you need to do is to generate the correct controlling value for the SELECT statement.