home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / turbopas / interupt.pas < prev    next >
Pascal/Delphi Source File  |  1994-03-05  |  10KB  |  245 lines

  1.                      Turbo interrupt handler code
  2.  
  3. I've had several requests for the Turbo interrupt handler installation
  4. code I mentioned a few weeks back, and I've been having increasing difficulty
  5. constructing return addresses. So, at the suggestion of several
  6. correspondents, I'm sending the code to you.
  7.  
  8. It provides two procedures which allow the installation and removal of
  9. ordinary Turbo procedures as interrupt handlers. Optionally the
  10. interrupt handler may use its own internal stack and chain the old
  11. interrupt vector on exit.
  12.  
  13. Procedures dealing with external interrupts will need to handle the 8259
  14. interrupt controller themselves. Since writing the code below I have
  15. sorted out more or less how to do this, and should there be any interest
  16. I'll put together a summary of what to do.
  17.  
  18. Jim Hague                jmh@ukc.uucp
  19. --------------------------------------------------------------------
  20. { A Turbo package to ease the use of interrupt routines written in
  21.   Turbo Pascal by allowing you to install ordinary Turbo procedures
  22.   as interrupt handlers without the need for gobs of inline statements
  23.   within each procedure.
  24.  
  25.   As far as the outside world is concerned, it provides
  26.  
  27.   type  interrupt_workspace ;
  28.  
  29.   procedure InstallInterruptHandler
  30.      ( routine_offset, intrrupt_no : integer ;
  31.        chain_old_vector, enable_ints_during, new_stack : boolean ;
  32.        var workspace : interrupt_workspace )
  33.  
  34.      This installs a Turbo procedure whose offset is routine_ptr
  35.      (ie ofs(RoutineName) ) to be activated by interrupt no intr_no.
  36.      Note that if the interrupt is an external one the 8259 is not
  37.      touched - you must do this yourself. If chain_old_vector is true
  38.      then the previous interrupt vector is chained to at the end of
  39.      the interrupt procedure. If enable_ints_during is true then
  40.      interrupts are enabled during the course of the interrupt
  41.      procedure. If new_stack is true then then the stack is switched
  42.      to an internal one within 'workspace' for the duration of the
  43.      interrupt (you won't need this only for interrupts called with
  44.      INT instructions when you know their stack has room enough).
  45.      Interrupts arriving at unpredictable times may arrive during
  46.      a DOS call and overflow the DOS stack, hence the need to switch
  47.      stacks.
  48.      Each interrupt must have a variable of type
  49.      interrupt_workspace accompanying it - it MUST appear in a
  50.      var declaration, not 'new'ed off the stack.
  51.  
  52.   procedure RemoveInterruptHandler
  53.      ( interrupt_no : integer ; var workspace : interrupt_workspace ) ;
  54.  
  55.      Removes the interrupt handler at interrupt no intr_no
  56.      that was installed by InstallInterruptHandler and replaces
  57.      it with the vector that was there previously, as held in
  58.      workspace. FEED THIS THE CORRECT WORKSPACE !
  59.  
  60.   Also provided are constants CLI and STI. Inline(CLI) will disable
  61.   interrupts until the next Inline(STI).
  62.  
  63.   *** NOTE ***
  64.      Interrupt routines installed with new_stack true (eg for where interrupts
  65.      may arrive during DOS) and all routines they call which in turn call
  66.      other routines MUST be compiled with the K- switch to disable stack
  67.      checking - since a new stack is in use it confuses Turbo, so off with
  68.      the checking. The stack depth with 'new_stack' is limited to
  69.      'intr_stack_size' bytes (see below), so be careful with over-generous
  70.      use of local variables or recursion.
  71.      Procedures responding to external interrupts will have to inform
  72.      the 8259 of the end of the interrupt themselves - issueing a generic
  73.      EOI via a Port[$20] := $20 should usually be sufficient.
  74.  
  75.   A summary of how it works.
  76.   ==========================
  77.  
  78.      Code is placed into link_code in workspace for each routine that
  79.      switches the stack if necessary, saves DS and SI on the stack,
  80.      places the offset of the routine being called into SI and the
  81.      current segment (the Turbo data segment) into DS, and then does
  82.      a far call to code in the typed constant (and thus in the code
  83.      segment) at goto_si. This saves the rest of the registers and does
  84.      a near call to the handling routine, which means the code generated
  85.      by the compiler to enter and exit the handling routine will work ok.
  86.      On exit it back to goto_si there is a far ret back to the code
  87.      in workspace which then restores DS and SI, restores the stack if
  88.      necessary and does either an IRET or a jump to the old vector
  89.      depending on the setup instructions.
  90.  
  91. }
  92.  
  93.  
  94.  
  95. const                                       { Actual constants }
  96.    intr_stack_size       = $40 ;            { Size of 'new_stack's }
  97.    max_link_code_size    = 60  ;            { Max size of link code space }
  98.  
  99.    STI                   = $fb ;            { Interrupt flag instructions }
  100.    CLI                   = $fa ;            { op codes }
  101.  
  102.  
  103. type
  104.    routine_ptr           = ^byte ;          { Any pointer really }
  105.    interrupt_workspace   = record
  106.                               link_code   : array [0 .. max_link_code_size]
  107.                                                   of byte ;
  108.                               old_routine : routine_ptr ;
  109.                               sp, ss      : integer ;
  110.                               int_stack   : array [0 .. intr_stack_size]
  111.                                                   of byte
  112.                            end ;
  113.  
  114. const                                       { Type const in code seg }
  115.    goto_si : array [1 .. 15] of byte =
  116.              ( $50,          { push ax }
  117.                $53,          { push bx }
  118.                $51,          { push cx }
  119.                $52,          { push dx }
  120.                $57,          { push di }
  121.                $06,          { push es }
  122.                $ff,$d6,      { call si }
  123.                $07,          { pop  es }
  124.                $5f,          { pop  di }
  125.                $5a,          { pop  dx }
  126.                $59,          { pop  cx }
  127.                $5b,          { pop  bx }
  128.                $58,          { pop  ax }
  129.                $cb ) ;       { ret far }
  130.  
  131.  
  132.  
  133. procedure InstallInterruptHandler
  134.    ( routine_offset, interrupt_no : integer ;
  135.      chain_old_vector, enable_ints_during, new_stack : boolean ;
  136.      var workspace : interrupt_workspace ) ;
  137.  
  138. var
  139.    i : integer ;
  140.    interrupt_vector : array [0 .. 255] of routine_ptr absolute 0:0 ;
  141.  
  142.    { Add a byte of code to the link, giving error if overflow }
  143.    procedure l ( b : byte ) ;
  144.    begin
  145.       if i > max_link_code_size then
  146.          begin
  147.             writeln ('Link code overflow') ;
  148.             halt (1) ;                      { Give error and stop }
  149.          end
  150.       else
  151.          begin
  152.             workspace.link_code[i] := b ;
  153.             i := i + 1
  154.          end
  155.    end ;
  156.  
  157.    { Add a word of code to the link (lo:hi), giving error if overflow }
  158.    procedure w ( w : integer ) ;
  159.    begin
  160.       l (lo(w)) ;
  161.       l (hi(w))
  162.    end ;
  163.  
  164. begin
  165.    i := 0 ;                                 { Reset link code buffer counter }
  166.    with workspace do
  167.       begin
  168.          { Code to swap stacks, if necessary }
  169.          if new_stack then
  170.             begin
  171.                l($2e) ; l($8c) ;       { mov  cs:[ss],ss        save SS }
  172.                l($16) ; w(ofs(ss)) ;
  173.                l($2e) ; l($a3) ;       { mov  cs:[sp],ax        save AX }
  174.                w(ofs(sp)) ;
  175.                l($8c) ; l($c8) ;       { mov  ax,cs             set SS to CS }
  176.                l($8e) ; l($d0) ;       { mov  ss,ax }
  177.                l($89) ; l($e0) ;       { mov  ax,sp             save SP and }
  178.                l($2e) ; l($87) ;       { xchg ax,cs:[sp]        recover AX }
  179.                l($06) ; w(ofs(sp)) ;
  180.                l($bc) ;                { mov  sp,<top of new stack> }
  181.                w(ofs(int_stack) + intr_stack_size)
  182.             end ;
  183.  
  184.          { Now save DS and SI and set them to CS and the routine offset }
  185.          l($56) ;                      { push si }
  186.          l($1e) ;                      { push ds }
  187.          l($0e) ;                      { push cs }
  188.          l($1f) ;                      { pop  ds }
  189.          l($be) ; w(routine_offset) ;  { mov  si,routine_offset }
  190.  
  191.          { Re-enable interrupts if desired }
  192.          if enable_ints_during then
  193.             l(STI) ;                   { sti }
  194.  
  195.          { Far call to the code at goto_si }
  196.          l($9a) ; w(ofs(goto_si)) ;    { call far goto_si }
  197.          w(Cseg) ;
  198.  
  199.          { Recover DS and SI }
  200.          l($1f) ;                      { pop  ds }
  201.          l($5e) ;                      { pop  si }
  202.  
  203.          { Reset stack to original if necessary }
  204.          if new_stack then
  205.             begin
  206.                l($2e) ; l($8e) ;       { mov  ss,cs:[ss]        recover SS }
  207.                l($16) ; w(ofs(ss)) ;
  208.                l($2e) ; l($8b) ;       { mov  sp,cs:[sp]        and SP }
  209.                l($26) ; w(ofs(sp))
  210.             end ;
  211.  
  212.          { Bung in either an IRET or a jump to next link }
  213.          if chain_old_vector then
  214.             begin
  215.                l($2e) ; l($ff) ;       { jmp far cs:[old_routine] }
  216.                l($2e) ; w(ofs(old_routine))
  217.             end
  218.          else
  219.             l($cf) ;                   { iret }
  220.  
  221.          { Now we can install the vector to link_code, saving the
  222.            old vector in old_routine. Interrupts are switched off
  223.            for this bit, just in case. }
  224.          inline (CLI) ;
  225.          old_routine := interrupt_vector[interrupt_no] ;
  226.          interrupt_vector[interrupt_no] := addr (link_code) ;
  227.          inline (STI)                  { Interrupts back on }
  228.       end
  229. end ;
  230.  
  231.  
  232.  
  233. procedure RemoveInterruptHandler
  234.    ( interrupt_no : integer ; var workspace : interrupt_workspace ) ;
  235. var
  236.    interrupt_vector : array [0 .. 255] of routine_ptr absolute 0:0 ;
  237. begin
  238.    inline (CLI) ;                      { Interrupts off }
  239.    interrupt_vector[interrupt_no] :=
  240.       workspace.old_routine ;
  241.    inline (STI)                        { and on again }
  242. end ;
  243.  
  244.  
  245.