     
                          EXCEPTION HANDLING ROUTINES
                                        
                                  _______ _ _                                   Version 0.6
                                 July 6th, 1988
                                        
                                 Gerald T HEWES
                               46 Blvd Inkermann
                               92220 NEUILLY S/S
     FRANCE 
     
                                 I INTRODUCTION
     
        The purpose of this module is to provide a programmer with a set of 
     easy to  use  routines  to  handle error conditions.  The principle of
     exception  handling  is  inspired  from  Ada(r)  exception   routines.
     Exception  handling helps to solve difficult error handling conditions
     such as No more memory, file not open, read/write error....    Usually
     because of the difficulty of handling these errors, not much action is 
     done to  correct them.  How many programmers faithfully test all their
     fread or fwrite results for possible errors?  
     
        Exception may be used by any program either as a  flow  control  or
     either  to  capture  software  errors  i.e 68000 exceptions and Traps.
     Exception makes the usage of traps very easy from C, since no assembly 
     code is required.  
     
        Experienced programmers might not find anything  really  new  about
     these  routines  and  may have already adopted ways to deal with error
     handling but I feel it might help recent Amiga programmers and anybody 
     who is not  interested  in  wrinting  for  the  (n+1)th  time  similar
     procedures.  
     
        These routines  are very different from the GOMF software.  You may
     have as many "protected code" zones in your program as you  want,  and
     you  dispose of a mean to propagate easily errors upward if you cannot
     fix the problem at a low level (usually the case for fwrites's).    In
     any case, no actions are taken whatsoever, control is simply passed to 
     your local  (context  wise)  handling  routine.    Your  task  is  not
     suspended.   All  your  handling  routines  are  in  user  mode   with
     multitasking enabled.  






















                                     - 0 -





     1.2 Contents 
     
     I   INTRODUCTION
     
     II  EXCEPTION HANDLING
     
          2.1 Introduction to Exception Handling
           2.1.1 Top Level
           2.1.2 Resource Protection
          2.2 How to Use Excption
           2.2.1 ExcpGlobal
           2.2.2 MAIN
           2.2.3 ExcpDeclare
           2.2.4 BEGIN/EXCEPTION/END
           2.2.5 Variables
           2.2.6 Excption Class Numbers
          2.3 Important Data
     
     III ORGANISATION
     
          3.1 Files
          3.2 Macros
     
     IV  IMPROVEMENTS
     
     V   CONCLUSION
     
     VI  BIBLIOGRAPHY
     
     






























                                     - 1 -





                             II EXCEPTION HANDLING
     
     2.1 Introduction to Exception Handling 
     
        Error  Management  and  complexe  human  interfaces  are  always  a
     difficult problem in  a  program,  and  no  perfect  solution  exists.
     Exception  handling  is one way to alleviate the problem, but does not
     solve it.    These  techniques  were  originally  applied  on  a   sun
     workstation for  an  interactive  program.   Port was then done on the
     Amiga, with a better interface.  Originally,  the  ideas  commes  from
     Ada's(r)  exception  handling  but  has been slightly modified to take
     into account C's differences.   A  first  difference  is  the  use  of
     Macros,  these  do  not have the power of defined keywords in Ada. The
     second major difference stems from the fact that Ada(r)  protects  you
     from  every  error  condition  possible,  while this library will only
     recuperate     errors     which     normally     give     birth     to
     "SoftWare Error/Task Held".  Thus  you still enjoy fireworks for fatal
     errors destroying the operation of the system.  
     
        What is exactly exception programing?  In gross  terms,  algorithms
     usually describe  the  normal  flow  of  operations.    Unfortunately,
     exceptional activities can happen and have to be treated.  If  nothing
     is  provided  by  the  language,  the programmer has to introduce many
     control statements to take into  account  these  abnormal  conditions.
     The  programer  usually  ends  up  with  a  code twice the size of the
     original algorithm.  This has two inconvenients: the code is no longer 
     clear because of the overhead, and it is hard to follow the flow of  a
     normal program.    Exception  programing  tries to solve this problem.
     The principle is clear, write the normal code, and add at the  end  of
     this code  the  code  describing  those exceptional events.  Each part
     being separate, programs get much clearer.  
     
        Lets consider a  simple  problem.    You  want  to  translate  from
     cartesian coordinates  to  polar coordinates.  The method in the first
     quadrant is theta = atan (y/x). This formula is true for x <> 0, if  x
     = 0, then theta = pi/2.  With no exceptions this gives: 
     























                                     - 2 -





        if (x != 0)
         {
           z     =  y/x;
           theta = atan(z);
         }
        else
         theta = pi/2;
     
        The normal  flow is lost in the if condition.  Exception programing
     would prefer: 
     
     ADA:
       begin
        z     := y/x;
        theta := atan(z);
       exception
        when NUMERIC_ERROR =>
         theta = pi/2;
        when others =>
         PUT("UNKNOWN ERROR");
         raise ERROR;
       end;
     
     This is simply translated into:
     
     C:
       BEGIN
        {
         z     = y/x;
         theta = atan(z);
        }
       EXCEPTION
        {
        switch(Eclass)
         {
          case ZERO_DIVIDE   : theta = pi/2; break;
          default            : puts("UNKNOWN ERROR"); RAISE(ERROR);
         }
        }
       END;
     
     
        This may not seem remarkable on a simple example, but it can become 
     very handy in real life problems which usually  are  not  coded  in  6
     lines.  
     
        The idea is simple.  In a protected region, you describe the simple 
     algorithm.  Automatic errors, or software detected ones can provoke an 
     "Exception"  which  will  cause  the  program to abandon the protected
     region to execute the handler region instead.  Either this handler  is
     qualified  to handle the exception and thus controls resumes after the
     protected/handler block, or the handler can decide  to  propagate  the
     exception to the next handler.  
     
        This is  the  second  advantage of Exception programing.  Protected
     blocks can be inscribed inside each other  like  russian  dolls.    An
     exception  can  thus  be  handled,  at  the most convenient level, and
     necessary action while "poping" out can be performed, like  liberating


                                     - 3 -





     some resources allocated in the normal flow.  
     
        This   is  why  Exception  handling  is  very  practical  in  riche
     environments like the amiga.  For example, you  can  isolate  all  the
     function calls  in  your  main  Wait  loop with a protection zone.  If
     anything happens, including an abort request, control resumes back  to
     your main  loop  where you can decide what to do.  Thus your main Wait
     loop can be seen as a "top level" for your  program.    Deep  in  your
     code,  if  you  get  stuck,  a  raise  to  the  "top  level" is always
     available.  
     
       Here are a few possible scenarios: 
     
     2.1.1 Top Level 
     
        forever
         {
           Wait();
            if (EXIT) break;
           BEGIN
            {
             switch() { Some Dangerous Functions }
            }
           EXCEPTION
            {
             switch(Eclass) { .... }
            }
           END
         }
     
     2.1.2 Resource Protection 
     
        In this construction, you cannot leave the area without  liberating
     your resource, even if the error will be handled at higher level.  
     
        Allocate-Resource
     
        BEGIN
         {
           Use-Resource (Dangerous Area)
           Liberate-Resource
         }
        EXCEPTION
         {
           Yell
           Liberate-Resource
           Propagate-Error-If-Necessary
         }
        END
     
     2.2 How to Use Excption 
     
        In  order  to  use  all the exception handling routines you need to
     include the following file: 
     
     #include "local:excption.h" 
     
        For examples of the use of the exception handler  consult  the  two


                                     - 4 -





     supplied examples "essai.c" and "fact.c".  
     
     2.2.1 ExcpGlobal 
     
        This static  definition is needed once in your code.  It reserves a
     structure needed by the handling  code  to  operate.    ExcpGlobal  is
     implemented as a macro.  
     
     2.2.2 MAIN 
     
        The  first  protected  block,  called throughout this document "top
     level block" is of a particular form.  This  block  does  not  need  a
     declaration  statement  of  the form ExcpDeclare, all relevant data is
     already declared in the ExcpGlobal structure.  The format of the first 
     protected is MAIN/EXCEPTION/OUT. The form  BEGIN/EXCEPTION/END  cannot
     be used.    This  is  very  important  because  the MAIN and OUT macro
     perform the necessary initialization and conclusion functions.  
     
        Here is an example: 
       main()
        {
          other declarations ...
     
          some code ...
     
          MAIN
           {
             protected code ...
           }
          EXCEPTION
           {
             Exception handling code ...
           }
          OUT
     
          yet more code ...
     
        }
     
        Note: the toplevel does not need to be in the main function.  
     
     2.2.3 ExcpDeclare 
     
        Declare an exception handling  routine  in  the  current  function.
     This  is  a  macro  which  hides  the  necessary declaration of hidden
     variables.  As such it must be  used  before  any  program  statement.
     Currently only one protected zone may exist in one routine.  
     
     2.2.4 BEGIN / EXCEPTION / END 
     
        Exception  handling  is  based on the notion of protected blocks of
     statements.   In  the  current  implementation,  only  one  block  per
     function is  allowed.    This in practice has not been an handicap and
     may be changed in future releases.  This limitation  only  stems  from
     the  Macros  used,  not  from any limitation by the exception handling
     routines.  The syntax is: 
     



                                     - 5 -





       toto()
        {
          ExcpDeclare;
          other declarations ...
     
          some code ...
     
          BEGIN
           {
             protected code ...
           }
          EXCEPTION
           {
             Exception handling code ...
           }
          END
     
          yet more code ...
     
        }
     
     In the some code block, nothing is changed, so put your faithful  code
     in this area.  No niceties are provided.  
     
     In  the  protected  code  block,  you  have  acces  to  the  following
     functions: 
     
        RAISE(ExcpClass)     : raise an error.
        BLOW(ExcpClass)      : raise/blow to top level handler.
     
        Control will be transferred to the exception handling block.   Your
     exception will  then be handled by this code.  Either the exception is
     propagated with a RAISE statement to upper levels, or  it  is  handled
     locally.   If  it is handled locally, execution will resume in the yet
     more code area.  If no exception occur in the  protected  code  block,
     control continues normally in the yet more code area.  
     
        These  two  function may also be called from any subroutines called
     in the protected code block.  They may not be called from another task 
     if a task is created in the protected code block.  
     
        A return statement is not allowed in the protected code  area,  and
     the exit function is not recommended.  Long Jumps are not recommended: 
     Exception handling  is  long jumping, stay consistent.  Goto's are not
     allowed to span in or out of the protected  code  block.    All  those
     limitations  come  from the fact that you have to execute the END code
     before leaving the function.   The  END  code,  disables  the  current
     context and  restores  the  previous  (upper)  one.    If  you want to
     LongJmp, you can modify your  code  to  use  instead  the  propagation
     mechanism.  
     
     In  the  handling code block you should test the exceptions and either
     handle them locally, or propagate them upward.  You have access to the 
     same functions: 
     





                                     - 6 -





       RAISE(ExcpClass)   : raise an error to the next level
       BLOW(ExcpClass)    : raise/blow to top level handler.
     
        RAISE has the same operation as when employed in the protected code 
     block, but will transfer control to  the  next  (upper)level  handling
     code, raising  the  error.    Once  raised,  there is no way to resume
     processing in the yet more code block.  
     
        The same restrictions apply to the handling code block  as  to  the
     protected block concerning: goto,return,exit,_exit,longjmp,...  
     
     2.2.5 Variables 
     
        The following variables are defined in the Handler routines: 
     
        Eclass    : Exception class of the exception
     
        In  the  exception  handling  zone,  Eclass  contains the exception
     number being treated.  Be sure to check this number because the system 
     may generate numbers that you have not planned for.  For  example  the
     system  traps  all  68000  errors,  Eclass  is then the Trap number as
     defined by Motorola.  This is very useful for program debugging  since
     the code traps bus errors which usually indicate pointer troubles.  
        If you do not handle the error locally, the call RAISE(Eclass) is a 
     must.  By doing this you can correct the exception at a higher level.  
     
     2.2.6 Excption Class Numbers 
     
        All  exceptions  have  an exception class number which serves as an
     identification.  Numbers from 1-65535 are reserved  values  which  may
     not be  used.  Under the current version words 1-1023 are reserved for
     68000 related problems.  Check the values defined  in  excption.h  for
     more information.   This range covers 68000 exception, 68000 traps and
     could cover 68000 interrupts.  Range 1024-2047  are  global  types  of
     error which   are  also  declared  in  exception.h.    Most  of  these
     declarations are inspired from Ada.  
        Range 2048-65535 are reserved for libraries.  These  values  cannot
     be used for an application.  
        Application must use numbers over 65536.  
     
        The  programmer must take great care to avoid using the same number
     for two different exception.  Unfortunately, this has to  be  done  by
     hand.  This is a major difference with Ada. Two, incompatible ways can 
     be used for allocating these exception class numbers.  
     
        The first method is to #define all your exception class numbers, in 
     a fashion  similar to the handler itself.  This is the most simple way
     but has the inconvenience of being dangerous.   Two  exceptions  might
     use the   same  number.    Coherence  has  to  be  maintained  by  the
     programmer.  This method is not recommended for large programs.  
     
        The   second   method   is   to   use   the   allocating   function
     AllocException().   AllocException  returns  a unique exception number
     starting from 65536. This automatically insures the coherence  of  all
     part of  a  large  program.  Its inconvenient is that it requires more
     code from the programmer point of view.  
     
        The current algorithm for allocation is rudimentary.  You can  only


                                     - 7 -





     call  AllocException  with  an argument of -1. This means, as for many
     Amiga allocating function, that you have no preference for the  number
     allocated.   Allocation  starts  from  65536, and increments by one at
     every call.  FreeAllocation is for decoration since it  does  nothing.
     I  might consider providing better allocation mechanism in the future,
     but for the moment I see no use for it.  
     
     2.3 Important Data 
     
        I  add  this  paragraph  since  I  do  not  have  the   appropriate
     documentation.  If you catch an error report it.  
     
        When  a  68000 exception occurs, the Amiga OS detects it and treats
     it.  The Excption 68000 number is calculated and pushed  down  on  the
     SSP stack  after  the  usual  exception  frame.  Control is then given
     back, in superuser mode, to the  code  pointed  by  task->tc_TrapCode.
     When  using  the  exception  handling  routines,  this  points  to  my
     assembler EIExcpHandler routine.  
        This routine tests if the code is running on a 68000. If  yes,  the
     SSP  stack  is  corrected  by 6+4 bytes for simple exception, and 14+4
     bytes for bus or address exceptions.  The +4 comes from the Long  Word
     pushed by the Amiga Os.  
        If  the  code  is  running  on  a  68010  or +, operations are very
     different.  The handler,  fetches  the  format  number  at  SSP+8  and
     corrects the SSP by the following values: 
     Values in 16-bit words 
     
                             Format
     
     0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
     -------------------------------------------------------------
     4   4   6   0   0   0   0   0  29  10  16  46   0   0   0   0
     
     
        As usual,  4  bytes have to be added to these numbers.  These table
     may be inaccurate since I do not have a good  documentation  on  these
     values.  
        After  correcting  the  SSP stack, the handler passes in User Mode,
     and calls the EIEndHandler (a C function) with  the  exception  number
     (provided by Amiga OS) as sole argument.  




















                                     - 8 -





                                III ORGANISATION
     
     
     3.1 Files 
     
        To   compile   programs  you  need  to  assign  local:  to  contain
     excption.lib, excption.h and boolean.h.  You must have directory named 
     private with the excppriv.h file in it.  Your programs need to include 
     the definition file "local:excption.h".  
     
        As of version 0.4, all  routines  are  compiled  seperately.    For
     Lattice users, these object modules are all joined in the excption.lib 
     lattice library.  To compile your code all you need to do id to add 
     LIB local:excption.lib 
     to your blink command.  Currently the files are organised as follows: 
     
     -excption.h   : file which your code needs to include to use the
                     exception routines.
     -boolean.h    : file included by excption.h
     -excption.lib : library file to use with blink.
     -excption.man : this file
     -excption.doc : documentation on defined functions
     -EI*.c        : source for defined functions
     -excphand.a   : assembler handling routine
     -essai.*      : A good example of use of exceptions
     -fact         : A bad example, for different reasons but instructive.
     -README       : text file describing the contents and the version
                     differences.
     
     3.2 Macros 
     
        This is a resume of the available Macros, Variables and Functions: 
     
     BEGIN        : M start of a protected block
     
     BLOW         : M give control to outmost handler
     
     Eclass       : V exception class
     
     END          : M end of handler block
     
     ExcpAlloc    : F allocate an unique exception class number
     
     ExcpDeclare  : M declare an excption block in a routine
     
     EXCPDISABLE  : M disable processor error handling
     
     EXCPENABLE   : M enable back processor handling (Default Mode)
     
     ExcpFree     : F free an allocated exception class number
     
     ExcpGlobal   : M declare a global structure needed by exception
     
     EXCEPTION    : M end of protected block, beginning of handler
     
     MAIN         : M start of outmost protected block (replaces BEGIN)
     
     OUT          : M end of outmost  protected block (replaces END)


                                     - 9 -





     
     RAISE        : M Propagate exception to inmost handler


























































                                     - 10 -





                                IV IMPROVEMENTS
     
        The following ideas will be investigated in further releases: 
     
     -Signaling to  the  User  that an 68000 exception has occured.  Useful
     for debugging.  
     
     -Final Catch of 68000 exceptions and user notice at the top level.  
     
     -Provide a mean for the program to call  a  supplied  function  before
     giving control to the local exception handling code 
     
     -Run time library version 
     
     -Stack checking before saving context 
     
     -Tracing of entrance into protected blocks in a debug version.  
     
        Discussing with  some  Ada compilers makers was instructive.  Their
     point of view was that they do not want  exception  handling  to  lose
     time in  normal  operations.    On  the other hand they are willing to
     consecrate a big amount of time to solve  where  the  control  has  to
     resume when  an exception is raised.  This is radically different from
     my implementation with longjmps.  Each BEGIN statement needs  to  save
     the environment which is slow.  When an exception is raised, restoring 
     the environment  is  not  costly.  Those Ada compiler makers have thus
     used a completely different approach.  At compile time they note where 
     each handler is active.  When an exception occurs, the  program  tests
     where the  exception  occured,  and  thus  where to branch.  It is not
     necessary to save the environment at each begin.  To achieve this  you
     need  to  have  exception handling defined in the language, not out of
     it.  Which is best?  
     
        Performances: 
     
        Version 0.5 seemed to have no  bugs.    There  are  two  things  to
     watch.   Bad  bugs  will  crash  the  system, because they destroy the
     environment.  Nothing can be done by software.  The exception  handler
     is fairly safe since its always checks its data coherence, if an error 
     occurs, an  exit(200) or exit(201) is performed.  These two exits have
     always been due to unmatched BEGIN/EXCEPTION/END statements,  even  in
     spots far from where the program stopped.  Check those if this happens 
     to you.  

















                                     - 11 -





                                  V CONCLUSION
     
     
        Exception  programming  is  a  very  powerful  tool,  but should be
     carefully used.  68000 Exceptions should stay exceptional.    Keep  in
     mind  that  an  exception  declaration  in a routine consumes about 40
     bytes in  the  stack.    Recursive  functions  have  to  be  carefully
     examined.   A good knowledge of LongJumping although not necessary can
     help resolve all kinds of conflicts you may run into.  This  code  can
     be  particularly helpful in the debugging period, but is unfortunately
     incompatible with Fred Fish DBUG macros.  In  general,  this  software
     does not like longjmps.  
     
        Because   of   its   similarity  with  Ada(r)  Exception  handling,
     consulting a manual on Ada, and mostly on how  to  use  its  exception
     mechanism is a recommended idea.  
     











































                                     - 12 -





                                VI BIBLIOGRAPHY
     
     * [ADA 83] 
     REFERENCE   MANUAL   FOR   THE   ADA(r)   PROGRAMMING   LANGUAGE   ---
     ANSI/MIL-STD 1815 A 
     * [BEHM 86] 
     NOTES SUR  LE  TRAITEMENT  DES  EXCEPTIONS  ---  Patrick  BEHM,  BULL,
     Louveciennes 
     
     
     
     
     
     
     
     
     
     
     
     
     
     (r)  ADA  is  a registered trademark of the U.S. Government. Ada Joint
     Program Office.  





































                                     - 13 -

