home *** CD-ROM | disk | FTP | other *** search
- Turbo interrupt handler code
-
- I've had several requests for the Turbo interrupt handler installation
- code I mentioned a few weeks back, and I've been having increasing difficulty
- constructing return addresses. So, at the suggestion of several
- correspondents, I'm sending the code to you.
-
- It provides two procedures which allow the installation and removal of
- ordinary Turbo procedures as interrupt handlers. Optionally the
- interrupt handler may use its own internal stack and chain the old
- interrupt vector on exit.
-
- Procedures dealing with external interrupts will need to handle the 8259
- interrupt controller themselves. Since writing the code below I have
- sorted out more or less how to do this, and should there be any interest
- I'll put together a summary of what to do.
-
- Jim Hague jmh@ukc.uucp
- --------------------------------------------------------------------
- { A Turbo package to ease the use of interrupt routines written in
- Turbo Pascal by allowing you to install ordinary Turbo procedures
- as interrupt handlers without the need for gobs of inline statements
- within each procedure.
-
- As far as the outside world is concerned, it provides
-
- type interrupt_workspace ;
-
- procedure InstallInterruptHandler
- ( routine_offset, intrrupt_no : integer ;
- chain_old_vector, enable_ints_during, new_stack : boolean ;
- var workspace : interrupt_workspace )
-
- This installs a Turbo procedure whose offset is routine_ptr
- (ie ofs(RoutineName) ) to be activated by interrupt no intr_no.
- Note that if the interrupt is an external one the 8259 is not
- touched - you must do this yourself. If chain_old_vector is true
- then the previous interrupt vector is chained to at the end of
- the interrupt procedure. If enable_ints_during is true then
- interrupts are enabled during the course of the interrupt
- procedure. If new_stack is true then then the stack is switched
- to an internal one within 'workspace' for the duration of the
- interrupt (you won't need this only for interrupts called with
- INT instructions when you know their stack has room enough).
- Interrupts arriving at unpredictable times may arrive during
- a DOS call and overflow the DOS stack, hence the need to switch
- stacks.
- Each interrupt must have a variable of type
- interrupt_workspace accompanying it - it MUST appear in a
- var declaration, not 'new'ed off the stack.
-
- procedure RemoveInterruptHandler
- ( interrupt_no : integer ; var workspace : interrupt_workspace ) ;
-
- Removes the interrupt handler at interrupt no intr_no
- that was installed by InstallInterruptHandler and replaces
- it with the vector that was there previously, as held in
- workspace. FEED THIS THE CORRECT WORKSPACE !
-
- Also provided are constants CLI and STI. Inline(CLI) will disable
- interrupts until the next Inline(STI).
-
- *** NOTE ***
- Interrupt routines installed with new_stack true (eg for where interrupts
- may arrive during DOS) and all routines they call which in turn call
- other routines MUST be compiled with the K- switch to disable stack
- checking - since a new stack is in use it confuses Turbo, so off with
- the checking. The stack depth with 'new_stack' is limited to
- 'intr_stack_size' bytes (see below), so be careful with over-generous
- use of local variables or recursion.
- Procedures responding to external interrupts will have to inform
- the 8259 of the end of the interrupt themselves - issueing a generic
- EOI via a Port[$20] := $20 should usually be sufficient.
-
- A summary of how it works.
- ==========================
-
- Code is placed into link_code in workspace for each routine that
- switches the stack if necessary, saves DS and SI on the stack,
- places the offset of the routine being called into SI and the
- current segment (the Turbo data segment) into DS, and then does
- a far call to code in the typed constant (and thus in the code
- segment) at goto_si. This saves the rest of the registers and does
- a near call to the handling routine, which means the code generated
- by the compiler to enter and exit the handling routine will work ok.
- On exit it back to goto_si there is a far ret back to the code
- in workspace which then restores DS and SI, restores the stack if
- necessary and does either an IRET or a jump to the old vector
- depending on the setup instructions.
-
- }
-
-
-
- const { Actual constants }
- intr_stack_size = $40 ; { Size of 'new_stack's }
- max_link_code_size = 60 ; { Max size of link code space }
-
- STI = $fb ; { Interrupt flag instructions }
- CLI = $fa ; { op codes }
-
-
- type
- routine_ptr = ^byte ; { Any pointer really }
- interrupt_workspace = record
- link_code : array [0 .. max_link_code_size]
- of byte ;
- old_routine : routine_ptr ;
- sp, ss : integer ;
- int_stack : array [0 .. intr_stack_size]
- of byte
- end ;
-
- const { Type const in code seg }
- goto_si : array [1 .. 15] of byte =
- ( $50, { push ax }
- $53, { push bx }
- $51, { push cx }
- $52, { push dx }
- $57, { push di }
- $06, { push es }
- $ff,$d6, { call si }
- $07, { pop es }
- $5f, { pop di }
- $5a, { pop dx }
- $59, { pop cx }
- $5b, { pop bx }
- $58, { pop ax }
- $cb ) ; { ret far }
-
-
-
- procedure InstallInterruptHandler
- ( routine_offset, interrupt_no : integer ;
- chain_old_vector, enable_ints_during, new_stack : boolean ;
- var workspace : interrupt_workspace ) ;
-
- var
- i : integer ;
- interrupt_vector : array [0 .. 255] of routine_ptr absolute 0:0 ;
-
- { Add a byte of code to the link, giving error if overflow }
- procedure l ( b : byte ) ;
- begin
- if i > max_link_code_size then
- begin
- writeln ('Link code overflow') ;
- halt (1) ; { Give error and stop }
- end
- else
- begin
- workspace.link_code[i] := b ;
- i := i + 1
- end
- end ;
-
- { Add a word of code to the link (lo:hi), giving error if overflow }
- procedure w ( w : integer ) ;
- begin
- l (lo(w)) ;
- l (hi(w))
- end ;
-
- begin
- i := 0 ; { Reset link code buffer counter }
- with workspace do
- begin
- { Code to swap stacks, if necessary }
- if new_stack then
- begin
- l($2e) ; l($8c) ; { mov cs:[ss],ss save SS }
- l($16) ; w(ofs(ss)) ;
- l($2e) ; l($a3) ; { mov cs:[sp],ax save AX }
- w(ofs(sp)) ;
- l($8c) ; l($c8) ; { mov ax,cs set SS to CS }
- l($8e) ; l($d0) ; { mov ss,ax }
- l($89) ; l($e0) ; { mov ax,sp save SP and }
- l($2e) ; l($87) ; { xchg ax,cs:[sp] recover AX }
- l($06) ; w(ofs(sp)) ;
- l($bc) ; { mov sp,<top of new stack> }
- w(ofs(int_stack) + intr_stack_size)
- end ;
-
- { Now save DS and SI and set them to CS and the routine offset }
- l($56) ; { push si }
- l($1e) ; { push ds }
- l($0e) ; { push cs }
- l($1f) ; { pop ds }
- l($be) ; w(routine_offset) ; { mov si,routine_offset }
-
- { Re-enable interrupts if desired }
- if enable_ints_during then
- l(STI) ; { sti }
-
- { Far call to the code at goto_si }
- l($9a) ; w(ofs(goto_si)) ; { call far goto_si }
- w(Cseg) ;
-
- { Recover DS and SI }
- l($1f) ; { pop ds }
- l($5e) ; { pop si }
-
- { Reset stack to original if necessary }
- if new_stack then
- begin
- l($2e) ; l($8e) ; { mov ss,cs:[ss] recover SS }
- l($16) ; w(ofs(ss)) ;
- l($2e) ; l($8b) ; { mov sp,cs:[sp] and SP }
- l($26) ; w(ofs(sp))
- end ;
-
- { Bung in either an IRET or a jump to next link }
- if chain_old_vector then
- begin
- l($2e) ; l($ff) ; { jmp far cs:[old_routine] }
- l($2e) ; w(ofs(old_routine))
- end
- else
- l($cf) ; { iret }
-
- { Now we can install the vector to link_code, saving the
- old vector in old_routine. Interrupts are switched off
- for this bit, just in case. }
- inline (CLI) ;
- old_routine := interrupt_vector[interrupt_no] ;
- interrupt_vector[interrupt_no] := addr (link_code) ;
- inline (STI) { Interrupts back on }
- end
- end ;
-
-
-
- procedure RemoveInterruptHandler
- ( interrupt_no : integer ; var workspace : interrupt_workspace ) ;
- var
- interrupt_vector : array [0 .. 255] of routine_ptr absolute 0:0 ;
- begin
- inline (CLI) ; { Interrupts off }
- interrupt_vector[interrupt_no] :=
- workspace.old_routine ;
- inline (STI) { and on again }
- end ;
-
-
-