📄 isr_8250.cpp
字号:
//
// ISR_8250.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.
//
// All of the code used in the 8250 interrupt service
// routine is found in this file. The Queue class inline
// functions are pulled in from QUEUE.H
#include <dos.h>
#include "pc8250.h"
#include "_pc8250.h"
#include "ascii.h"
// Prototypes for the internal handlers called by the ISR.
void handle_modem_status_interrupt( struct isr_data_block *data );
void handle_tx_interrupt( struct isr_data_block *data );
void handle_rx_interrupt( struct isr_data_block *data );
// This is the main body of the 8250 interrupt handler. It
// sits in a loop, repeatedly reading the Interrupt ID
// Register, and dispatching a handler based on the
// interrupt type. The line status interrupt is so simple
// that it doesn't merit its own handler.
void isr_8250( struct isr_data_block * data )
{
_enable();
for ( ; ; ) {
switch( inp( data->uart + INTERRUPT_ID_REGISTER ) & 7 ) {
case IIR_MODEM_STATUS_INTERRUPT :
handle_modem_status_interrupt( data );
break;
case IIR_TX_HOLDING_REGISTER_INTERRUPT :
handle_tx_interrupt( data );
break;
case IIR_RX_DATA_READY_INTERRUPT :
handle_rx_interrupt( data );
break;
case IIR_LINE_STATUS_INTERRUPT :
data->ls_int_count++;
data->line_status |= inp( data->uart + LINE_STATUS_REGISTER );
break;
default :
return;
}
}
}
// The modem status interrupt handler has to do three
// things. It has to handle RTS/CTS handshaking. It has
// to handle DTR/DSR handshaking, and it has to update the
// modem_status member of the isr_data structure.
void handle_modem_status_interrupt( struct isr_data_block *data )
{
data->ms_int_count++;
data->modem_status =
(unsigned int)
inp( data->uart + MODEM_STATUS_REGISTER );
if ( data->handshaking & rts_cts )
if ( data->modem_status & MSR_DELTA_CTS ) // Has CTS changed?
if ( data->modem_status & MSR_CTS ) {
if ( data->blocked & rts_cts ) {
data->blocked &= ~rts_cts;
jump_start( data );
}
} else {
if ( !( data->blocked & rts_cts ) )
data->blocked |= rts_cts;
}
if ( data->handshaking & dtr_dsr )
if ( data->modem_status & MSR_DELTA_DSR )
if ( data->modem_status & MSR_DSR ) {
if ( data->blocked & dtr_dsr ) {
data->blocked &= ~dtr_dsr;
jump_start( data );
}
} else {
if ( !( data->blocked & dtr_dsr ) )
data->blocked |= dtr_dsr;
}
}
// The TX interrupt is fairly simple. All it has to do is
// transmit the next character, if one is available. Depending
// on whether or not a character is available, it will set or
// clear the tx_running member. Note that here and in
// jump_start(), the handshake_char gets first shot at going
// out. This is normally an XON or XOFF.
void handle_tx_interrupt( struct isr_data_block *data )
{
int c;
data->tx_int_count++;
if ( data->send_handshake_char >= 0 ) {
outp( data->uart + TRANSMIT_HOLDING_REGISTER,
data->send_handshake_char );
data->send_handshake_char = -1;
} else if ( data->blocked ) {
data->tx_running = 0;
} else {
c = data->TXQueue.Remove();
if ( c >= 0 )
outp( data->uart + TRANSMIT_HOLDING_REGISTER, c );
else
data->tx_running = 0;
}
}
// The RX interrupt handler is divided into two nearly
// independent sections. The first section just reads in the
// character that has just been received and stores it in a
// buffer. If the UART type is a 16550, up to 16 characters
// might be read in. The next section of code handles the
// possibility that a handshaking trigger has just occurred,
// modifies any control lines or sends an XOFF as needed.
void handle_rx_interrupt( struct isr_data_block *data )
{
int c;
int mcr;
int lsr;
data->rx_int_count++;
// The receive data section
for ( ; ; ) {
c = inp( data->uart + RECEIVE_BUFFER_REGISTER );
if ( data->handshaking & xon_xoff ) {
if ( c == XON ) {
data->blocked &= ~xon_xoff;
jump_start( data );
return;
} else if ( c == XOFF ) {
data->blocked |= xon_xoff;
return;
}
}
if ( !data->RXQueue.Insert( (char) c ) )
data->overflow = 1;
if ( data->uart_type == UART_8250 )
break;
lsr = inp( data->uart + LINE_STATUS_REGISTER );
data->line_status |= lsr;
if ( ( lsr & LSR_DATA_READY ) == 0 )
break;
}
// The handshaking section
if ( data->handshaking ) {
if ( data->RXQueue.InUseCount() > HighWaterMark ) {
if ( ( data->handshaking & rts_cts ) &&
!( data->blocking & rts_cts ) ) {
mcr = inp( data->uart + MODEM_CONTROL_REGISTER );
mcr &= ~MCR_RTS;
outp( data->uart + MODEM_CONTROL_REGISTER, mcr );
data->blocking |= rts_cts;
}
if ( ( data->handshaking & dtr_dsr ) &&
!( data->blocking & dtr_dsr ) ) {
mcr = inp( data->uart + MODEM_CONTROL_REGISTER );
mcr &= ~MCR_DTR;
outp( data->uart + MODEM_CONTROL_REGISTER, mcr );
data->blocking |= dtr_dsr;
}
if ( ( data->handshaking & xon_xoff ) &&
!( data->blocking & xon_xoff ) ) {
data->blocking |= xon_xoff;
if ( data->send_handshake_char == XON ) {
data->send_handshake_char = -1;
} else {
data->send_handshake_char = XOFF;
jump_start( data );
}
}
}
}
}
// Any time transmit interrupts need to be restarted, this
// routine is called to do the job. It gets the interrupts
// running again by sending a single character out the TX
// register manually. When that character is done
// transmitting, the next TX interrupt will start. The
// tx_running member of the class keeps track of when we can
// expect another TX interrupt and when we can't.
void jump_start( struct isr_data_block *data )
{
int c;
// Both tx_running and blocked can change behind my back in the
// ISR, so I have to disable interrupts if I want to be able to
// count on them.
_disable();
if ( !data->tx_running ) {
if ( ( c = data->send_handshake_char ) != -1 )
data->send_handshake_char = -1;
else if ( !data->blocked )
c = data->TXQueue.Remove();
if ( c >= 0 ) {
outp( data->uart, c );
data->tx_running = 1;
}
}
_enable();
}
// *********************** END OF ISR_8250.CPP ***********************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -