home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1993 May
/
SIMTEL_0593.ISO
/
msdos
/
turbopas
/
interupt.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-04-19
|
10KB
|
245 lines
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 ;