version : 29/5/90.

   ************************************************************

   * The XBRA stuff in this pack is free to use, PD and ...   *

   *          <include: please don't sell source>             *

   *          <include: I take no responsability>             *

   * You are free to do anything ('cept selling) with the     *

   * pack, which contains :                                   *

   *        XBRA.TXT       This text and your suggestions.    *

   *        XBRAC.S        The Assembly source.               *

   *        XBRAC.O        Object file.                       *

   *        XBRAC.H        Header file.                       *

   *                                                          *

   *        XBRADEMO.C     Demo program (sic).                *

   *        XBRADEMO.TOS   Compiled version.                  *

   *        XBRA.PRJ       Project used to compile demo.      *

   *        Doc's in the source!                              *

   *                                                          *

   ************************************************************







MOTIVATION

==========

   The XBRA protocol is the standard way of taking system vectors, it 
was created so that programs could coexist with other programs. 
Pretty easy you might say, but when two or more programs have hooked 
themselves on the same vector, there are big problems if they want 
out. 



   VECTOR                 ROUTINE 1                  ROUTINE 2

/-----------\      /--------------------\

| routine 1 | ---> | code for routine 1 |

\-----------/      <         .          <

                    >        :           >     /--------------------\

                   | JUMP to routine 2  | ---> | code for routine 2 |

                   \--------------------/      <         .         <

                                                >        :          >

                                               | JUMP to routine 3  |

                                               \--------------------/



   Now if routine 1 wants out, it simply writes the address of 
routine 2 into the vector - but what if routine 2 wants out, where 
does it put the address of routine 3 ?

- In the vector I guess, and thereby unchaines routine 1...



   The XBRA protocol places the address of the next routine at a 
fixed offset from the entry of the routine, it also adds two 
identifiers, one telling "I know about XBRA" and the other one 
telling "OK, I'm here" :



           /--------------------\

xb_magic:  |       "XBRA"       |      :                    :

           +--------------------+       >         :          >

xb_id:     |       "_ID_"       |      |     Routine_3      | --->

           +--------------------+      +--------------------+

xb_oldvec: |     Routine_2      | ---> | code for routine 2 |

           +--------------------+      <         .         <

xb_entry:  | code for routine 1 |       >        :          >

           <         .          <      :                    :

            >        :           >

           | JUMP (xb_oldvec)   |

           \--------------------/



   The 'xb_magic' is a constant long with the pattern "XBRA", and the 
'xb_id' is the identification of your routine. 'xb_oldvec' points to 
the next routine in the chain.

   Now, if all routines hanging on a vector follow the XBRA protocol, 
it will be possible to safely unchain oneself, without destroying 
anything vital.









IMPLEMENTATION

==============

   Ok, Now you might understand the problem with the XBRA protocol, 
it demands a control over the codegeneration which only an assembler 
gives...

   The main ide is to have a general routine which conforms to the 
XBRA protocol. This routine will save the registers, call the your 
routine and then call the next one in the chain.



The XBRA structure looks as follows:



/-----------------------------------------------------------------\

|   xb_magic : dc.b "XBRA"                                        |

|   xb_id    : dc.b "xxxx"          ;Your ID.                     |

|   xb_oldvec: ds.l 1               ;Storage for Old vector.      |

|   xb_entry :                                                    |

|        MOVEM.L D0-D2/A0-A1, -(SP)                               |

|        JSR routine                ;Call routine.                |

|        MOVEM.L (SP)+, D0-D2/A0-A1                               |

|        MOVE.L xb_oldvec(PC), -(SP);Push 'xb_oldvec'.            |

|        RTS                        ;Call next routine in chain.  |

\-----------------------------------------------------------------/



   You will need one of these beasts for each vector you intercept, 
but you need only fill the 'xb_magic' and 'xb_id', the actual code is 
copied into the struct when you install your routine.







HOW TO USE THE MODULE

=====================

There are 4 entry points:

      XB_isinstal(), XB_install(), XB_remove(), XB_kill().



Entry points :

-------------



int XB_isinstal(char *magic, long vector);

++++++++++++++++++++++++++++++++++++++++++

   If 'magic' is in XBRA chain 'vector' : return 1

NOTE : This can be called with an 8 byte 'magic' which contains the

       "XBRA" and '_id_' only!

NOTE : Make sure that the 'magic' struct is word-aligned!





int XB_install (char *magic, long vector, void *routine);

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

   Installs an userdefined routine.

NOTE : Make sure that the 'magic' struct is word-aligned!





int XB_remove(char *magic, long vector);

++++++++++++++++++++++++++++++++++++++++

   Removes the routine from the vector, returns 1 if the routine 
could be found.

NOTE : This can be called with an 8 byte 'magic' which contains the

       "XBRA" and '_id_' only!

NOTE : Make sure that the 'magic' struct is word-aligned!





int XB_kill(char *magic, long vector);  (the -9 option!)

++++++++++++++++++++++++++++++++++++++

   Like 'XB_remove' but will allways remove the routine from the 
vector (also if a non XBRA routine has taken the vector [- this will 
also be killed]).

   This routine depends on the fact that 'Magic' contains the 
'Oldvec' field. It uses this to unchain all the routines after the 
last XBRA routine until your routine.

NOTE : This _must_ be called with the same 'magic' struct as when the

       routine was installed...



Parameters :

   magic:

      Is a pointer to a ~32 bytes struct, with the string "XBRA" and 

   followed by your 4 byte id-string : ex. "_id_".

   NOTE : Make sure that the 'magic' struct is word-aligned!



   vector:

      This is the address of the vector to intercept.



   routine:

      This is a pointer to your routine.



Return val :

      All functions uses the 'XB_isinstal', and they returns 1 if the

   routine could be found in the chain. This means that 'XB_remove'

   returns 1 if everything was OK (because the "id" was found), and

   'XB_install' returns 0 (because the "id" not was found)...







EXAMPLE :

=========

   To illustrate how simple it is, here is an example:

You want to install a 'clock' on the system vektor at 0x400, all you 
have to do is to make a 32 byte array:

                  /------------------------------\

                  | char magic[32] = "XBRA_id_"; |

                  \------------------------------/



where '_id_' is your id. The rest of this array is used to hold a 
small assembly routine. Install the routine on the vector :

                /----------------------------------\

                | XB_install(magic, 0x400, clock); |

                \----------------------------------/



where 'clock' is the name of the routine. This call makes a copy of 
the assembly routine into the array [ -don't modify the array after 
this call]. 'XB_install' returns 1 if a routine with your ID allready 
was in the chain (yours was not installed). Otherwise your routine 
will now be called 200 times every second!

   To remove the routine again, simply make the call :

                    /--------------------------\

                    | XB_remove(magic, 0x400); |

                    \--------------------------/

this again will return 1, iff your routine was found (and now 
unchained), 0 otherwise...

   If you are sure your routine is in the chain and 'XB_remove' 
returns 0, you can kill it with 'XB_kill' this function relayes on 
the variables in the 'magic' struct (it'd better be installed or 
...).









NOTES & HINTS

=============

   To make the 'Magic' struct word-aligned, the most simple way is to 
define the struct just after an 'int' or 'long' (never char or short 
int), you could also define the 'Magic' struct as an array of 8 
longs, where the two first longs was the value of "XBRA" and your id.

   This is mainly meant as a warning... (I have used many hours 
trying to find such an error, (my struct was 33 bytes [to be on the 
safe side :-) ], and I had two of those beasts after each other, 
first one worked allright, but the other one...)



   Make sure to be in supervisor mode when installing routines on the 
system vectors, look in the demo-program, if you are unfamiliar with 
this.



   There are some minor short comings, one being that the pack is 
mainly for Borland TC, but you are free to modify the source to run 
with your favourite C compiler.

   The routines relies on some TC specific things :

       o All functions shall preserve D3-D7/A2-A7.

         -the functions in my pack only uses D0-D2/A0-A1.

    [  o First pointer parameters are passed in A0, A1.    ]

    [  o First simple parameters are passed in D0, D1, D2. ]

    [  o All C-functions preserve D3-D7/A2-A7.             ]



Things in [] can be customized :

   In this version of the source there is a switch, which enables 
TC-like parameters. If the switch 'TurboC' is set to 1, all 
parameters is passed in the registers, else they are passed on the

stack.

   I have added a REG directive which tells what registers that the 
compiler consider *free*. Free registers is the ones that the 
compiler _don't_ save when entering a function. The code which calls 
your C-routine shall save these, to ensure that the interupted 
program don't break.

   If your compiler don't consider D0,D1,D2,A0 and A1 free, then you 
are in for a major rewrite. [D2 is easy to remove, as it only appears 
in one function as a local variable].



   If you fix shortcomings, or rewrite the source to other compilers, 
please let me know (in the spirit of this posting)...

   I am prepared to help with the converting to other compilers, tell 
me about your compiler and assembler.

   Also if you find bugs or have any suggestions please let me 
know...



    _________________________________________________________

   / Now we need a way to get rid of those programs floating \

   \         around in the memory (suggestions???)           /

    \_______________________________________________________/



 - Klaus 





You can contact me on one of the following addresses :

         (micro@imada.dk).      OR         Klaus Pedersen

                                           Stammen 53\15

                                           DK-5220 Odense SO



