📄 pcirq.cpp
字号:
//
// PCIRQ.CPP
//
// Source code from:
//
// Serial Communications: A C++ Developer's Guide, 2nd Edition
// by Mark Nelson, IDG Books, 1999
//
// Please see the book for information on usage.
//
// This module contains the interrupt management code.
// ConnectToIRQ() is called to establish an interrupt handler
// function, DisconnectFromIRQ() is called to break the
// connection.
#include <dos.h>
#include "pcirq.h"
typedef void ( __far __interrupt *HANDLER )( void );
// Prototypes for all the handlers defined here
void __far __interrupt isr2( void );
void __far __interrupt isr3( void );
void __far __interrupt isr4( void );
void __far __interrupt isr5( void );
void __far __interrupt isr7( void );
void __far __interrupt isr10( void );
void __far __interrupt isr11( void );
void __far __interrupt isr15( void );
void __far __interrupt int1b( void );
void __far __interrupt int23( void );
// When any IRQs are hooked by one of our routines, the IRQ code
// disables control-break termination by taking over the two
// control-break vectors. The saved control-break state is
// stored in the following three variables.
static HANDLER old_int1b;
static HANDLER old_int23;
static unsigned char old_dos_break_state;
// A count of the number of handlers currently in use.
static int count = 0;
// This structure keeps track of the state of each of the eight
// possible IRQ lines our program can take over. This includes
// the address of the new handler, the old handler, and most
// importantly, the data pointer passed to the new handler when
// it is invoked.
struct {
enum irq_name irq;
void *isr_data;
void ( *isr_routine)( void *isr_data );
HANDLER handler;
HANDLER old_isr;
int old_pic_enable_bit;
} irq_data[] = { { IRQ2, 0, 0, isr2, 0, 0 },
{ IRQ3, 0, 0, isr3, 0, 0 },
{ IRQ4, 0, 0, isr4, 0, 0 },
{ IRQ5, 0, 0, isr5, 0, 0 },
{ IRQ7, 0, 0, isr7, 0, 0 },
{ IRQ10, 0, 0, isr10, 0, 0 },
{ IRQ11, 0, 0, isr11, 0, 0 },
{ IRQ15, 0, 0, isr15, 0, 0 }
};
// All of the new ISR handlers are called when the interrupt
// occurs. All they do is call the hooked routine, passing it
// a pointer to the data block it asked for in the
// ConnectToIRQ() routine. When this is done, they take care
// of issuing the EOI instruction and then exiting. The
// "return 1" is necessary for Zortech's interrupt handlers.
// If a "return 0" is used, the handler will then chain to the
// old interrupt, which we don't want.
void __far __interrupt isr2( void )
{
irq_data[ 0 ].isr_routine( irq_data[ 0 ].isr_data );
_disable();
outp( 0x20, 0x20 );
}
void __far __interrupt isr3( void )
{
irq_data[ 1 ].isr_routine( irq_data[ 1 ].isr_data );
_disable();
outp( 0x20, 0x20 );
}
void __far __interrupt isr4( void )
{
irq_data[ 2 ].isr_routine( irq_data[ 2 ].isr_data );
_disable();
outp( 0x20, 0x20 );
}
void __far __interrupt isr5( void )
{
irq_data[ 3 ].isr_routine( irq_data[ 3 ].isr_data );
_disable();
outp( 0x20, 0x20 );
}
void __far __interrupt isr7( void )
{
irq_data[ 4 ].isr_routine( irq_data[ 4 ].isr_data );
_disable();
outp( 0x20, 0x20 );
}
// These routines have to send an EOI to the second 8250 PIC
// as well as the first.
void __far __interrupt isr10( void )
{
irq_data[ 5 ].isr_routine( irq_data[ 5 ].isr_data );
_disable();
outp( 0xa0, 0x20 );
outp( 0x20, 0x20 );
}
void __far __interrupt isr11( void )
{
irq_data[ 6 ].isr_routine( irq_data[ 6 ].isr_data );
_disable();
outp( 0xa0, 0x20 );
outp( 0x20, 0x20 );
}
void __far __interrupt isr15( void )
{
irq_data[ 7 ].isr_routine( irq_data[ 7 ].isr_data );
_disable();
outp( 0xa0, 0x20 );
outp( 0x20, 0x20 );
}
// The two control-break vectors do nothing, so that Control-C
// and Contrl-Break both have no effect on our program.
void __far __interrupt int1b( void )
{
}
void __far __interrupt int23( void )
{
}
// This utility routine is only used internally to these
// routines. It sets control of the given interrupt number to
// the handler specifified as a parameter. It returns the
// address of the old handler to the caller, so it can be
// stored for later restoration. Note that Zortech stores the
// old handler internally, so we just return a 0.
HANDLER HookVector( int interrupt_number, HANDLER new_handler )
{
union REGS r;
struct SREGS s = { 0, 0, 0, 0 };
HANDLER old_handler = 0;
r.h.al = (unsigned char) interrupt_number;
r.h.ah = 0x35;
int86x( 0x21, &r, &r, &s );
*( (unsigned __far *) old_handler + 1 ) = s.es;
*( (unsigned __far *) old_handler ) = r.x.bx;
s.ds = FP_SEG( new_handler );
r.x.dx = FP_OFF( new_handler );
r.h.al = (unsigned char) interrupt_number;
r.h.ah = 0x25;
int86x( 0x21, &r, &r, &s );
return old_handler;
}
// When we are done with an IRQ, we restore the old handler
// here. Note once again that Zortech does this internally, so
// we don't have to.
void UnHookVector( int interrupt_number, HANDLER old_handler )
{
union REGS r;
struct SREGS s = { 0, 0, 0, 0 };
s.ds = FP_SEG( old_handler );
r.x.dx = FP_OFF( old_handler );
r.h.al = (unsigned char) interrupt_number;
r.h.ah = 0x25;
int86x( 0x21, &r, &r, &s );
}
// When we have taken over an interrupt, we don't want keyboard
// breaks to cause us to exit without properly restoring
// vectors. This routine takes over the DOS and BIOS
// control-break routines, and sets the DOS BREAK flag to 0.
// The old state of all these variables is saved off so it can
// be restored when the last interrupt routine is restored.
void TrapKeyboardBreak( void )
{
union REGS r;
old_int1b = HookVector( 0x1b, int1b );
old_int23 = HookVector( 0x23, int23 );
r.h.ah = 0x33;
r.h.al = 0;
int86( 0x21, &r, &r );
old_dos_break_state = r.h.dl;
r.h.ah = 0x33;
r.h.al = 1;
r.h.dl = 0;
int86( 0x21, &r, &r );
}
// When the last interrupt is restored, we can set the
// control-break vectors back where they belong, and restore
// the old setting of the DOS break flag.
void RestoreKeyboardBreak( void )
{
union REGS r;
UnHookVector( 0x1b, old_int1b );
UnHookVector( 0x23, old_int23 );
r.h.ah = 0x33;
r.h.al = 1;
r.h.dl = old_dos_break_state;
int86( 0x21, &r, &r );
}
// When connecting to an IRQ, I pass it an irq number, plus a
// pointer to a function that will handle the interrupt. The
// function gets passed a pointer to a data block of its choice,
// which will vary depending on what type of interrupt is being
// handled.
RS232Error
ConnectToIrq( enum irq_name irq,
void *isr_data,
void ( *isr_routine )( void *isr_data ) )
{
int i;
int pic_mask;
int pic_address;
int interrupt_number;
int temp;
for ( i = 0 ; ; i++ ) {
if ( irq_data[ i ].irq == irq )
break;
if ( irq_data[ i ].irq == IRQ15 )
return RS232_ILLEGAL_IRQ;
}
if ( irq_data[ i ].isr_routine != 0 )
return RS232_IRQ_IN_USE;
if ( count++ == 0 )
TrapKeyboardBreak();
irq_data[ i ].isr_data = isr_data;
irq_data[ i ].isr_routine = isr_routine;
pic_mask = 1 << ( irq % 8 );
if ( irq < IRQ8 ) {
pic_address = 0x20;
interrupt_number = irq + 8;
} else {
interrupt_number = irq + 104;
pic_address = 0xa0;
}
irq_data[ i ].old_isr = HookVector( interrupt_number,
irq_data[ i ].handler );
temp = inp( pic_address + 1 );
irq_data[ i ].old_pic_enable_bit = temp & pic_mask;
outp( pic_address + 1, temp & ~pic_mask );
return RS232_SUCCESS;
}
// This routine restores an old interrupt vector.
int DisconnectFromIRQ( enum irq_name irq )
{
int i;
int pic_mask;
int pic_address;
int interrupt_number;
int temp;
for ( i = 0 ; ; i++ ) {
if ( irq_data[ i ].irq == irq )
break;
if ( irq_data[ i ].irq == IRQ15 )
return 0;
}
if ( irq_data[ i ].isr_routine == 0 )
return 0;
irq_data[ i ].isr_data = 0;
irq_data[ i ].isr_routine = 0;
pic_mask = 1 << ( irq % 8 );
if ( irq < IRQ8 ) {
pic_address = 0x20;
interrupt_number = irq + 8;
} else {
interrupt_number = irq + 104;
pic_address = 0xa0;
}
temp = inp( pic_address + 1 );
temp &= ~pic_mask;
temp |= irq_data[ i ].old_pic_enable_bit;
outp( pic_address + 1, temp );
UnHookVector( interrupt_number, irq_data[ i ].old_isr );
if ( --count == 0 )
RestoreKeyboardBreak();
return 1;
}
// ******************** END OF PCIRQ.CPP ********************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -