delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1999/10/05/15:24:45

From: "Martin Peach" <martin AT loplop DOT com>
Newsgroups: comp.os.msdos.djgpp
Subject: Guide: Intercepting And Processing Hardware Interrupts
Date: Tue, 5 Oct 1999 12:24:38 -0400
Organization: Communications Accessibles Montreal, Quebec Canada
Lines: 741
Message-ID: <7td8nn$kqj@tandem.CAM.ORG>
NNTP-Posting-Host: dialup-514.hip.cam.org
X-Newsreader: Microsoft Outlook Express 4.72.3110.5
X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
To: djgpp AT delorie DOT com
DJ-Gateway: from newsgroup comp.os.msdos.djgpp
Reply-To: djgpp AT delorie DOT com

Following is an edited version of Peter Marinov's guide which is on the
www.delorie.com website (apologies for a lengthy post, but this seems the
best way to publish it). The original is written in a rather stilted form of
english so I thought I would just clean it up a bit...
I corrected some minor typos and things. The main problem I ran into is
this: Marinov claims that the DPMI server wants the interrupts disabled on
returning from an IRQ. The code has a STI instruction, which I understand to
mean SET INTERRUPT FLAG, thus enabling CPU innterrupts. So I changed that. I
also don't beleive that the DPMI server gives a damn whether your interrupts
are enabled or not, but I am not sure. Does anyone feel on solid ground
talking about this?
I hope this is useful to someone.
\/\/\/*= Martin.


<html><head><title>Guide: Intercepting And Processing Hardware
Interrupts</title>
<base href="http://www.delorie.com/djgpp/doc/ug/interrupts/hwirqs.html">
</head><body><center><small><a href="/19990805-net.html">delorie.com network
changes</a></small></center>
<center><script
src="http://www2.burstnet.com/cgi-bin/ads/ad1954a.cgi/RETURN-CODE/JS/64"></s
cript><noscript><a
href="http://www2.burstnet.com/ads/ad1954a-map.cgi/64"><img
src="http://www2.burstnet.com/cgi-bin/ads/ad1954a.cgi/64" ismap border=0
width=468 height=60 alt="delorie.com is funded by banner
ads."></A></noscript><table width=100% border=0 cellspacing=0
cellpadding=3><tr><td align=left valign=top bgcolor="#ffcc99"><small><font
face="itc avant garde gothic,helvetica,arial"><b> &nbsp;
<a href="/" target="_top">www.delorie.com</a>/<a href="/djgpp/"
target="_top">djgpp</a>/<a href="/djgpp/doc/" target="_top">doc</a>/<a
href="/djgpp/doc/ug/"
target="_top">ug</a>/interrupts/hwirqs.html</b></font></small></td>
<td align=right valign=top bgcolor="#ffcc99"><small><font face="itc avant
garde gothic,helvetica,arial"><b> &nbsp;
<a href="/search/">search</a> &nbsp;
</b></font></small></td>
</tr></table><big><big><b>Guide: Intercepting And Processing Hardware
Interrupts</b></big></big></td></p></center>


<p>Written by <a href="mailto:mar22 AT usa DOT net">Peter Marinov</a>.</p>
<p>Edited by <a href="mailto:martin AT loplop DOT com">Martin Peach</a>.</p>

<h2>PART I</h2>

<p>Beginner's guide. This describes what
hardware interrupts are and how are they processed by an IBM-compatible PC.
If you have already worked with handling hardware interrupts you can skip
this part.</p>

<h3>1. First acquaintance with hardware interrupts.</h3>

<p>Contemporary computers maintain many and varied devices --
hard disk, video card, keyboard, mouse, printer etc. Most of the time
these devices work autonomously and require no special CPU
attention.

<p>For example, the keyboard needs to inform the CPU only when a key is
pressed or released; most of the time the CPU is unaware of what is
happening
with this device. When a key is pressed the keyboard REQUESTS special
attention from the CPU; when the current instruction is completed the
processor
temporarily discontinues executing the main program and starts ("vectors
to") a
special chunk of code which will process the keyboard's request. This piece
of code
obtains the code representing the key that was pressed and informs the
keyboard
device that the information has been processed, which enables the keyboard
to
perform a new request when a new key is available. When the request has been
resolved the main program continues executing from precisely the point where
it was interrupted.

<p>Such a request has a special name, <i>Interrupt Request or
IRQ.</i> The CPU maintains a special table with addresses for 15
interrupt requests. The chunk of code resolving an interrupt request
is usually called a <i>driver</i>. Such a driver is in most cases
provided by the operating system, the device manufacturer, or, rarely,
by the BIOS.

<h3>2. Hardware interrupt controller - 8259.</h3>

<p>As more than one device can request an interrupt at any time, there
is a special controller provided in every IBM PC compatible, the Intel
8259 Interrupt Controller (actually there are two of them, and in most
modern PCs they have been incorporated into a so-called "chipset" which
incorporates several I/O-related subsystems into one physical package).
The 8259 serves two purposes in general: to accumulate requests and to
convey them one by one to the CPU. Reading the complete documentation of
this controller will easily distract you from the IRQ processing material,
but following the 8259 processes using examples will make it easier
to understand the nature of IRQs. Let us have two requests at one
and the same time: the first will be the PC timer and the second will be the
keyboard. Each device makes a request to the 8259 for CPU time. The 8259
accepts the requests in a fixed order of priority. First in line is the
timer whose interrupt has higher priority than that of the keyboard. When
the
timer driver serves the request it issues an end-of-interrupt command
to the 8259, which directs the controller to signal the next lower proprity
interrupt, in this case the keyboard, to the CPU. The keyboard interrupt in
turn sends the same end-of-interrupt command, which then makes the 8259
available to accumulate further interrupt requests. Below is how an example
of how timer and keyboard drivers might look in a piece of code.

<pre>
void TimerDriver(void)
{
  ++TimerCount;
  outportb(0x20, inportb(0x20));   /* End-Of-Interrupt command */
}

void KbdDriver(void)
{
  unsigned char control_kbd;

  LastKey = inportb(0x60);

  /* Tell the keyboard that the key is processed */
  control_kbd = inportb(0x61);
  outportb(0x61, control_kbd | 0x80);  /* set the "enable kbd" bit */
  outportb(0x61, control_kbd);  /* write back the original control value */
  outportb(0x20, inportb(0x20));  /* End-Of-Interrupt command */
}
</pre>

<p>It is not necessary to actually know how the End-Of-Interrupt command
works, you only need to know that such a command is necessary for the
successful accomplishment of a hardware interrupt.

<p>The CPU can be directed not to serve hardware interrupts by calling
the function <tt>disable()</tt>. A call to <tt>enable()</tt> will re-enable
the interrupts at the CPU level. The 8259 can be directed not to convey
specific device interrupt requests to the CPU. The example below
demonstrates
the enabling and disabling of timer interrupts.

<pre>
void DisableTimer(void)
{
  outportb(0x21, inportb(0x21) | 1);/* mask off the timer interrupt */
}

void EnableTimer(void)
{
  outportb(0x21, inportb(0x21) &amp; ~1);
}
</pre>

<p>For each device connected to the 8259 controller there is a bit
which when set to 1 disables and when set to 0 enables this particular
device. From a software point of view this is all that needs to be known
for the 8259 interrupt controller. Below is a table showing interrupt
priorities for a typical PC. The devices 0-7 are connected to the primary
8259 and 8-15 are connected to the secondary interrupt controller. The
secondary interrupt controller can be accessed via ports 0xA0 and 0xA1.

<pre>
 0 System timer
 1 Keyboard
 2 Cascaded second interrupt controller
 3 COM2 - serial interface
 4 COM1 - serial interface
 5 LPT - parallel interface
 6 Floppy disk controller
 7 Available
 8 CMOS real-time clock
 9 Sound card
10 Network adapter
11 Available
12 Available
13 Numeric processor
14 IDE -- Hard disk interface
15 IDE -- Hard disk interface
</pre>

<p>The map of interrupt sources may vary depending on the
installed devices and their configuration.

<h2>PART II</h2>

<p>Advanced guide to handling hardware interrupts
under a 32-bit DPMI server.<p>

<h3>3. IRQ handling in both real mode and 32-bit protected-mode (under
DPMI).</h3>

<p>When compiled using DJGPP your program runs in 32-bit
protected-mode, but since DOS is a real-mode operating system, a
special kind of executive is needed to provide an environment for 32-bit
programs -- the DPMI server (DPMI stands for DOS Protected Mode
Interface). While providing smooth execution of 32-bit code, the DPMI
host switches to real-mode to execute DOS API code, and switches
back to protected-mode to continue executing your code.

<p>IRQs are handled differently in real and
protected mode. The 80x86 family of CPUs maintain an interrupt vector
table starting at 0x0:0x0. The DOS API provides two functions to attach
and detach interrupt handlers for a specific vector in this table. As
part of the process of indicating to the CPU that an IRQ needs service
the 8259 controller issues an interrupt vector. Using this information
the CPU fetches the address of the appropriate driver routine. The
execution of the current program is temporarily discontinued
("interrupted").
The CPU stores its current address and flag status onto the stack and then
starts to execute the driver code. When the driver has finished handling
the interrupt the CPU continues execution of the current program by
retrieving the current address and flags from the stack. The 8259
vectors are mapped in the CPU interrupt vector table starting by default at
#8.

<p>When running in 32-bit mode the CPU maintains an Interrupt
Descriptor Table (IDT) which differs from the interrupt vector table
used in real mode. The DPMI host directs the CPU to use the IDT instead
of the interrupt vector table. The list of drivers is kept in a virtual
interrupt vector table. When an interrupt occurs, the host checks this
virtual interrupt vector table and, if the IRQ is to be served by
a protected mode driver, the CPU starts to execute 32-bit code. The DPMI
host
guarantees that, even when an IRQ occurs while the CPU is executing
real-mode
code, the correct 32-bit driver will be executed. If your program runs
in any kind of v86 mode  environment (EMM386 or QEMM in config.sys, or
under a Windows DOS prompt) an IRQ will be served much faster if
the driver is 32-bit code.

<p>The DPMI host provides virtual memory, meaning that portions of
code or data memory can be "paged out" to a swap file on a disk when
not in use, and brought back when this particular code or data is
requested by the program in the course of its execution. As DOS
is not reentrant in its API functions the disk operations are not
reentrant either. Which, in other words, means you should never call
disk read/write operations while the previous one has not terminated.
This prevents 32-bit driver code being randomly read
in or paged out to a disk swap file. The DPMI host provides a set of
functions to ensure that a specified portion of code or data should
be excluded from the paging process, by designating those memory regions
<i>locked</i>.
All function and data portions accessed by the driver code should be
locked before claiming an IRQ, otherwise the code might not be there
when the interrupt routine is called.

<p>When invoking your driver code to handle an IRQ the DPMI host provides
only a bare minimum of stack space. It is your responsibility to switch
to larger space for stack use. Your driver should save upon
entering and restore upon exiting all CPU registers and flags. All
segment registers should then be loaded with their proper selectors in
order to access driver data (and stack).

<h3>4. Writing a <i>wrapper</i> for a hardware interrupt handler.</h3>

<p>As there is a list of tasks that every interrupt handling routine
should perform on receiving a request and again on finishing the request,
a special <i>wrapper</i> module will be presented below. Understanding
this module needs special knowledge in 80386 assembler and AT&amp;T style
assembler. However, if you are not in possesion of such advanced skills,
you may consider this module a black box, as its functionality will be
described in the best possible detail.

<p>The wrapper's functionality is described in exactly 9 steps.

<ol>
<li> Save all 80386 registers whose contents might be destroyed by the IRQ
handler
<li> Point all segment registers to the handler's data segment by
loading the proper selector
<li> Point the stack pointer register at somewhere with enough stack space
<li> Invoke the user's IRQ handler
<li> Restore the original stack settings
<li> Restore all 80386 registers
<li> If necessary call the original IRQ handler (the one that was there
before the program started)
<li> Reenable interrupts as expected by the DPMI server
<li> Exit with "return from interrupt" instruction
</ol>

<p>All the steps revealed in details:

<ol>

<li> Save all 80386 registers. This is accomplished by simply pushing all
the registers onto the stack. On exit they will all be popped back.
<li> Load the segment registers. In DJGPP all segments by default point
to the same segment (flat memory). Thus all of them may be loaded with a
single selector value. DJGPP provides a cs-relative variable that is always
an IRQ secured selector -- <tt>__djgpp_ds_alias</tt>.
<li> Let the stack pointer sp point to an area with enough stack space.
The wrapper should be provided with a table of readily allocated stacks
--
<tt>_IRQStacks</tt>. The wrapper searches for an available stack. If
successful, it marks the stack as used; this will prevent other IRQs from
allocating the same stack. If the stack search is not successful, the
wrapper exits leaving the IRQ unserved. This may lead to computer lock-up,
which is why a sufficient number of stacks should be prepared in
<tt>_IRQStacks</tt>.
<li> Call the proper IRQ handler. The IRQ handler call-back functions
should be set in the <tt>_IRQHandlers</tt> array. As the wrapper knows its
own IRQ number, the proper handler is invoked.
<li> Restore the original stack frame; the old stack frame was stored in
the new stack. Pop the old frame and load ss:sp with this value.
<li> Restore all 80386 CPU registers. Pop the registers from stack.
<li> The handler is a call-back function of the form <tt> int
IRQHandler(void)</tt>. If the handler returns 1, the default IRQ
handler is to be called. Before attaching a particular wrapper to an
interrupt vector, the old value should be stored in the
<tt>_OldIRQVectors</tt>
table. The wrapper jumps to a particular old vector instead of
proceeding with step 9; the original old handler will finish the interrupt
request with a "return-from-interrupt" intstruction. Before jumping,
the CPU registers are restored from the stack.
<li> Enable the interrupts before returning to DPMI. DPMI expects the
interrupts to be enabled upon returning.
<li> Finish with a "return-from-interrupt" instruction.

</ol>

<p>Below is the actual code of a wrapper module.

<pre>
/* wrap_g.S */

/*
IRQ wrappers for DJGPP.
*/

.data
_IRQWrappers:
        .long   _IRQWrap0,  _IRQWrap1,  _IRQWrap2,  _IRQWrap3
        .long   _IRQWrap4,  _IRQWrap5,  _IRQWrap6,  _IRQWrap7
        .long   _IRQWrap8,  _IRQWrap9,  _IRQWrap10, _IRQWrap11
        .long   _IRQWrap12, _IRQWrap13, _IRQWrap14, _IRQWrap15

_IRQHandlers:
        .long   0, 0, 0, 0      /* 0 - 3 */
        .long   0, 0, 0, 0      /* 4 - 7 */
        .long   0, 0, 0, 0      /* 8 - 11 */
        .long   0, 0, 0, 0      /* 12 - 15 */

        .globl  _IRQWrappers
        .globl  _IRQHandlers
        .globl  _IRQWrap
        .globl  _IRQWrap_End

/*
How many stacks to allocate for the irq wrappers. You could
probably get away with fewer of these, if you want to save memory and
you are feeling brave...
Extracted from irqwrap.h: BOTH SHOULD BE THE SAME!
*/
#define IRQ_STACKS      8

.text
#define IRQWRAP(x)      ; \
_IRQWrap##x:       ; \
        pushw   %ds                     /* save registers */ ; \
        pushw   %es      ; \
        pushw   %fs      ; \
        pushw   %gs      ; \
        pushal       ; \
        /* __djgpp_ds_alias is irq secured selector (see exceptn.h) */ ; \
        movw    %cs:___djgpp_ds_alias, %ax   ; \
        movw    %ax, %ds                /* set up selectors */ ; \
        movw    %ax, %es     ; \
        movw    %ax, %fs     ; \
        movw    %ax, %gs     ; \
        ; \
        movl    $(IRQ_STACKS - 1), %ecx /* look for a free stack */ ; \
        /* Search from the last toward the first */  ; \
StackSearchLoop##x:      ; \
        leal    _IRQStacks(, %ecx, 4), %ebx   ; \
        cmpl    $0, (%ebx)     ; \
        jnz     FoundStack##x           /* found one! */ ; \
        ; \
        decl    %ecx                    /* backward */  ; \
        jnz     StackSearchLoop##x    ; \
        ; \
        jmp     GetOut##x               /* No free stack! */ ; \
        ; \
FoundStack##x:       ; \
        movl    %esp, %ecx              /* save old stack in ecx:dx */ ; \
        movw    %ss, %dx     ; \
        ; \
        movl    (%ebx), %esp            /* set up our stack */ ; \
        movw    %ax, %ss     ; \
        ; \
        movl    $0, (%ebx)              /* flag the stack is in use */ ; \
        ; \
        pushl   %edx                    /* push old stack onto new */ ; \
        pushl   %ecx      ; \
        pushl   %ebx      ; \
        ; \
        cld                             /* clear the direction flag */ ; \
        ; \
        movl    _IRQHandlers + 4 * x, %eax   ; \
        call    *%eax                   /* call the C handler */ ; \
        ; \
        popl    %ebx                    /* restore the old stack */ ; \
        popl    %ecx      ; \
        popl    %edx      ; \
        movl    %esp, (%ebx)     ; \
        movw    %dx, %ss     ; \
        movl    %ecx, %esp     ; \
        ; \
        orl     %eax, %eax              /* check return value */; \
        jz      GetOut##x     ; \
        ; \
        popal                           /* chain to old handler */; \
        popw    %gs      ; \
        popw    %fs      ; \
        popw    %es      ; \
        popw    %ds      ; \
                                        /* 8 = sizeof(__dpmi_paddr) */; \
        ljmp    %cs:_OldIRQVectors + 8 * x   ; \
        ; \
GetOut##x:       ; \
        popal                           /* iret */  ; \
        popw    %gs      ; \
        popw    %fs      ; \
        popw    %es      ; \
        popw    %ds      ; \
        sti  /* set CPU interrupt enable flag */ ; \
        iret

_IRQWrap:
        .byte   0

IRQWRAP(0);
IRQWRAP(1);
IRQWRAP(2);
IRQWRAP(3);
IRQWRAP(4);
IRQWRAP(5);
IRQWRAP(6);
IRQWRAP(7);
IRQWRAP(8);
IRQWRAP(9);
IRQWRAP(10);
IRQWRAP(11);
IRQWRAP(12);
IRQWRAP(13);
IRQWRAP(14);
IRQWRAP(15);

_IRQWrap_End:
        .byte   0
</pre>

<p>The file is "wrap_g.S". 'S' is capital to direct gcc to provide
preprocessor support for the file. The wrapper is defined as a macro
and then multiplicated to generate code for all the 16 IRQ wrappers.
All the wrappers' addresses fill an array <tt>(_IRQWrappers) </tt>so
that a
particular wrapper could be simply picked out by its index in this
array.

<h3>5. Code and data locking.</h3>

<p>To prevent the code and data accessed by an IRQ
handler from being paged out, the memory should be locked. The DPMI
server provides
a <tt>__dpmi_lock_linear_region()</tt> function for locking memory
regions.
Below is the code for the <tt>LockData()</tt> and <tt>LockCode()</tt>
functions.

<pre>
int LockData(void *a, long size)
{
  unsigned long baseaddr;
  __dpmi_meminfo region;

  if (__dpmi_get_segment_base_address(_my_ds(), &amp;baseaddr) == -1)
    return (-1);

  region.handle = 0;
  region.size = size;
  region.address = baseaddr + (unsigned long)a;

  if (__dpmi_lock_linear_region(&amp;region) == -1)
    return (-1);

  return (0);
}

int LockCode(void *a, long size)
{
  unsigned long baseaddr;
  __dpmi_meminfo region;

  if (__dpmi_get_segment_base_address(_my_cs(), &amp;baseaddr) == -1)
    return (-1);

  region.handle = 0;
  region.size = size;
  region.address = baseaddr + (unsigned long)a;

  if (__dpmi_lock_linear_region(&amp;region) == -1)
    return (-1);

  return (0);
}
</pre>

<p><tt>_my_cs()</tt> and <tt>_my_ds() </tt>return the code and data
segment selectors respectively. The region description structure is filled
with start and end addresses, and then
<tt>__dpmi_lock_linear_region()</tt> is invoked.

<p>Locking a variable
or array looks like this: <tt>LockData(&amp;var, sizeof(var))</tt>; but
how to lock a function when the code size is unknown? When compiled, two
adjacent functions occupy two adjacent memory regions. Now calculating
function code size becomes simple address arithmetic. Below is an
example of how to lock an IRQ handler function.

<pre>
void TimerDriver(void)
{
  ++TimerCount;
  outpotb(0x20, inportb(0x20));  /* End-Of-Interrupt command */
}

void EndOfTimerDriver(void)
{
}

...

LockCode(TimerDriver, (long)EndOfTimerDriver - (long)TimerDriver);
</pre>

<p>It is useful to use macros to facilitate data and code locking.

<pre>
#define END_OF_FUNCTION(x)    static void x##_End() { }

#define LOCK_VARIABLE(x)  LockData((void *)&amp;x, sizeof(x))
#define LOCK_FUNCTION(x)  LockCode(x, (long)x##_End - (long)x)
</pre>

<p>Below is the same example of function and data locking, this time using
macros.

<pre>
long TimerCount;

void TimerDriver(void)
{
  ++TimerCount;
  outportb(0x20, inportb(0x20));  /* End-Of-Interrupt command */
}
END_OF_FUNCTION(TimerDriver);

...

LOCK_FUNCTION(TimerDriver);
LOCK_VARIABLE(TimerCount);
</pre>

<h3>6. Intercepting IRQs, allocating stacks (additional service
functions).</h3>

<p>Intercepting IRQs involves a pair of DPMI functions --
<tt>__dpmi_get_protected_mode_interrupt_vector()</tt> and
<tt>__dpmi_set_protected_mode_interrupt_vector()</tt>. The original
vector value is stored in <tt>OldIRQVectors[]</tt>, to be used by the
wrapper
to invoke the old handler, and to be restored when
<tt>UninstallIRQ()</tt> is called. The wrapper will invoke the proper
user code based on what is loaded in <tt>IRQHandlers[]</tt>. The proper IRQ
wrapper is picked from <tt>IRQWrappers[]</tt>, which is initialized in
wrap_g.S. The setup function <tt>InitIRQ()</tt> is called once only, to
prepare the library for proper use. <tt>UninstallIRQ()</tt> keeps
count of how much IRQ vectors are hooked, and when the last one is
released, a <tt>ShutDownIRQ()</tt> cleanup function is invoked to
release stack space from the heap. Notice that interrupt vectors for
the second (cascaded) IRQ controller are mapped starting at position
0x70. Depending on the IRQ number the correct vector will intercepted.

<pre>
int InstallIRQ(int nIRQ, int (*IRQHandler)(void))
{
  int nIRQVect;
  __dpmi_paddr IRQWrapAddr;

  if (!bInitIRQ)
    if (!InitIRQ())
      return 0;

  if (nIRQ &gt; 7)
    nIRQVect = 0x70 + (nIRQ - 8);
  else
    nIRQVect = 0x8 + nIRQ;

  IRQWrapAddr.selector = _my_cs();
  IRQWrapAddr.offset32 = (int)IRQWrappers[nIRQ];
  __dpmi_get_protected_mode_interrupt_vector(nIRQVect,
&OldIRQVectors[nIRQ]);
  IRQHandlers[nIRQ] = IRQHandler;  /* IRQWrapper will call IRQHandler */

  __dpmi_set_protected_mode_interrupt_vector(nIRQVect, &IRQWrapAddr);
  return 1;
}

void UninstallIRQ(int nIRQ)
{
  int nIRQVect;
  int i;

  if (nIRQ &gt; 7)
    nIRQVect = 0x70 + (nIRQ - 8);
  else
    nIRQVect = 0x8 + nIRQ;

  __dpmi_set_protected_mode_interrupt_vector(nIRQVect,
&OldIRQVectors[nIRQ]);
  IRQHandlers[nIRQ] = NULL;

  /*
  Check whether all the IRQs are uninstalled and call ShutDownIRQ().
  */
  for (i = 0; i &lt; 16; ++i)
    if (IRQHandlers[i] != NULL)
      return;  /* Still remains a handler */
  ShutDownIRQ();
}
</pre>

<p><tt>InitIRQ()</tt> and <tt>ShutDownIRQ()</tt> are a pair of
functions, called when <tt>InstallIRQ()</tt> is first called and when
<tt>UninstallIRQ </tt> unhooks the last vector that was intercepted.
The InitIRQ() function locks all the data and code memory accessed from an
IRQ
call, allocates space for stacks, and sets a flag indicating that the
module is properly set up. The ShutDownIRQ() function disposes of the stack
space.

<pre>
static int InitIRQ(void)
{
  int i;

  /*
  Lock IRQWrapers[], IRQHandlers[] and IRWrap0()-IRQWrap15().
  */
  if (LOCK_VARIABLE(IRQWrappers) == -1)
    return 0;
  if (LOCK_VARIABLE(IRQHandlers) == -1)
    return 0;
  if (LOCK_VARIABLE(OldIRQVectors) == -1)
    return 0;
  if (LOCK_FUNCTION(IRQWrap) == -1)
    return 0;

  for (i = 0; i &lt; IRQ_STACKS; ++i)
  {
    if ((IRQStacks[i] = malloc(STACK_SIZE)) == NULL)
    ...
    LockData(IRQStacks[i], STACK_SIZE) == -1
    ...
    (char *)IRQStacks[i] += (STACK_SIZE - 16);  /* Stack is incremented
downward */
  }
  bInitIRQ = 1;
  return 1;
}

static void ShutDownIRQ(void)
{
  int i;
  char *p;

  for (i = 0; i &lt; IRQ_STACKS; ++i)
  {
    p = (char *)IRQStacks[i] - (STACK_SIZE - 16);
    free(p);
  }
  bInitIRQ = 0;
}
</pre>

<h3>7. Acknowledgements (you may consider this "for further reading" as
well).</h3>

<p>

<a href="http://www.talula.demon.co.uk/">Allegro</a>, A game programming
library originated by Shawn Hargreaves<br>

Alaric B. Williams, <a
href="http://www.abwillms.demon.co.uk/prog/djints.txt">The Dark Art of
writing DJGPP Hardware Interrupt Handlers</a>

<!-- fill in when we know the URL
<h3>8. Source code.</h3>

<p>Complete source code, a makefile and a small test program is
available
for downloading at xxx.
-->




<br clear=both></p>
<table width=100% border=0 cellspacing=0 cellpadding=3><tr>
<td align=left valign=top bgcolor="#ffcc99"><small><font face="itc avant
garde gothic,helvetica,arial"><b> &nbsp;
<a href="/users/dj/" target="_top">webmaster</a> &nbsp;
<a href="/donations.html" target="_top">donations</a> &nbsp;
<a href="/store/books/" target="_top">bookstore</a> &nbsp;
</b></font></small></td>
<td align=right valign=top bgcolor="#ffcc99"><small><font face="itc avant
garde gothic,helvetica,arial"><b> &nbsp;
<a href="/" target="_top">delorie software</a> &nbsp;
<a href="/privacy.html" target="_top">privacy</a> &nbsp;
</b></font></small></td>
</tr><tr><td align=left valign=top bgcolor="#ffcc99"><small><font face="itc
avant garde gothic,helvetica,arial"><b> &nbsp;
<a href="/copyright.html" target="_top">Copyright © 1998</a> &nbsp;
<a href="/users/dj/" target="_top">by DJ Delorie</a> &nbsp;
</b></font></small></td>
<td align=right valign=top bgcolor="#ffcc99"><small><font face="itc avant
garde gothic,helvetica,arial"><b> &nbsp;
Updated Nov 1998 &nbsp;
</b></font></small></td>
</tr></table>
</center>
<p align=center><small><i>You can help support this site by
visiting the advertisers that sponsor it! (only once each,
though)</i></small></p>
</body></html>







- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019