% Single line comments start with "%".  Multi-line comments are enclosed
% within tags of "%{}%".

% The Reflex Game Program

%{
  To illustrate the RTOS like behavior of Esterel, we use an auxiliary
  module AVERAGE to compute the average reflex time.

  AVERAGE and REFLEX_GAME are considered to be two separate programs 
  communicating via the reception and emission of signals.

  The AVERAGE module's purpose is to receive integers and to broadcast the
  average of the integers received so far. The communication with AVERAGE
  involves two signals: 

  * INCREMENTAVERAGE(integer): input of the AVERAGE module; provokes the
    incrementation of the current average value by the conveyed integer 

  * AVERAGE_VALUE(integer): output of the AVERAGE module; broadcasts the
    current average value.

    The new average value is emitted synchronously with any input. 

    You must realize that the signal AVERAGE_VALUE is undefined up to the
    first reception of INCREMENT_AVERAGE. Reading an undefined signal is
    flagged as an error by the simulator, but not by the compiler. 

  Note that you have played the game enough when the AVERAGE module
  crashes with a division by zero error.  This happens if MEASURE_NUMBER
  is set high enough (unlikely) to cause the value of a integer to wrap
  around to zero.  TOTAL is also likely to wrap around giving wrong
  results.

  We don't try to trap those here for the sake of clarity in this simple
  example.  This shows that Esterel can produce code that crashes if you
  get complacent in your requirements and specifications.  No programming
  language yet has over come the human ability to specify things that are
  wrong.

  Esterel's strength is its robust real time signal manipulation,
  not its numerical manipulation.
}%

module AVERAGE:

% Instruct the Esterel compiler to generate #include "reflex_game.h":

type ForceTheIncludeDirective;

input INCREMENT_AVERAGE : integer;

output AVERAGE_VALUE : integer;

var TOTAL := 0 : integer,
  NUMBER := 0 : integer in
  every immediate INCREMENT_AVERAGE do
    TOTAL := TOTAL + ?INCREMENT_AVERAGE;
    NUMBER := NUMBER + 1;
    emit AVERAGE_VALUE(TOTAL / NUMBER)
  end every
end var

end module

% AUTOMATON ENGINE:

module REFLEX_GAME:

% CONSTANTS:

constant LIMIT_TIME : integer;
constant MEASURE_NUMBER : integer;
constant PAUSE_LENGTH : integer;

%{
  We need to wait for a random time. To determine the delay length, we call an
  external function RANDOM.

  Notice that such a "function" is somewhat improper in Esterel, since
  functions should be deterministic. To be perfectly clean, we could send a
  signal START_TIMER to an external random timer and wait for a TIME_EXPIRED
  reply. However such a use of some random number generator is obviously
  standard practice even in deterministic languages.
}%

function RANDOM() : integer;

% INPUT FUNCTIONS:

input MS;
input COIN;
input READY;
input STOP;

% OUTPUT ACTIONS:

output DISPLAY : integer;
output GO_ON;
output GO_OFF;
output GAME_OVER_ON;
output GAME_OVER_OFF;
output TILT_ON;
output TILT_OFF;
output RING_BELL;

%{
  [Note: The 'relation' directive is a hold over from older compiler
     versions. Esterel-Technologies V5 or later compiler and all of CEC's,
     do not have such a restriction and in fact simply ignore the
     'relation' directives.
  ]

  Although it is not strictly necessary, we shall assume the following
  incompatibility relations between input signals.
}%

relation MS # COIN # READY;
relation COIN # STOP;
relation READY # STOP;

% REFLEX_GAME proper starts at this point.

% Overall initializations, such as setting up hardware at power up: 

% Display zero on the display, turn GO and TILT LED off, GAME_OVER LED on:

emit DISPLAY(0);
emit GO_OFF;
emit TILT_OFF;
emit GAME_OVER_ON;

% Loop over a single game:

every COIN do

% Initializations, done every time we insert a coin for a new game:

  emit DISPLAY(0);
  emit GO_OFF;
  emit GAME_OVER_OFF;
  emit TILT_OFF;

  % Exception handling:

  trap END_GAME,
       ERROR in
     signal INCREMENT_AVERAGE : integer,
     AVERAGE_VALUE : integer in
     [

     %{
       We use the AVERAGE module via a 'run' instruction, putting it in
       parallel with the body of the END_GAME trap construct.

       Since AVERAGE is effectively copied inside the body of the main
       "every COIN" loop via 'run', it is restarted afresh whenever
        a new coin is inserted.
     }%

     run AVERAGE

     % Parallelize 'Average' module and rest of game:

     ||

     repeat MEASURE_NUMBER times

       % Phase-1: waiting for the READY button: 
       %{
         During phase 1, we wait for the player to press READY, watching
         the time limit of LIMIT_TIME milliseconds and ringing the bell
         whenever STOP is pressed. If the timeout elapses, we exit the
         ERROR trap: 
       }%

       do
        do
         every STOP do
         emit RING_BELL
           end every
         upto READY

       watching LIMIT_TIME MS timeout
        exit ERROR
       end timeout;

       % Phases 2 and 3: 

       trap END_MEASURE in
        [

        %{
          There is something in common between phases 2 and 3: pressing
          the  READY  button rings  the  bell. This  is  conveniently
          expressed using  a parallel construct, putting the following
          statement in parallel with the phase2/phase3 sequence:
        }%

        every READY do
            emit RING_BELL
        end every

        ||

        % Phase-2: waiting for RANDOM MS 

        %{ 
          During phase 2, we wait for a random number of milliseconds,
          raising ERROR if STOP is pressed before the end of the delay.
          In other words we wait for a random delay  watching STOP and
          exit ERROR in case of timeout: 
        }%

        do
         await RANDOM() MS
          watching STOP timeout
           exit ERROR
          end timeout;

        %{ 
          Notice that the Phase-2 code is the "dual" of the Phase-1 code:
          the pairs READY-MS of Phase-1 and MS-STOP of Phase-2 play the
          same role. This illustrates the advantage of defining temporal
          units for all signals, and not just some predefined physical
          time clock. 

          Notice also that ERROR is exited if the player presses STOP
          simultaneously with the end of the delay, according to the
          semantics of the watching statement. 
        }%

        emit GO_ON;

        % Phase-3: waiting for the STOP button 

        %{ 
          We wait for STOP with a time limit of LIMIT_TIME milliseconds,
          counting the milliseconds. The structure is similar to that of
          Phase-1, except that we must declare a variable to hold the time: 
        }%

        do
         var TIME := 0 : integer in

          do
            every MS do
              TIME := TIME + 1
            end every
          upto STOP;

          emit DISPLAY(TIME);

          % Send the AVERAGE module the time via a integer signal:

          emit INCREMENT_AVERAGE(TIME)

        end var

        watching LIMIT_TIME MS timeout
         exit ERROR
        end timeout;

        emit GO_OFF;
        exit END_MEASURE

       ]
      end trap
    end repeat;

    % Final display:

    await PAUSE_LENGTH MS do

    %{
      Display the results of the average value that came from the AVERAGE
      module via its output signal:
    }%

    emit DISPLAY(?AVERAGE_VALUE)

   end await;
  exit END_GAME
  ]
 end signal

 handle ERROR do
    emit TILT_ON;
    emit GO_OFF
 end trap;

 emit GAME_OVER_ON

end every

end module