_C Programming Column_ by Al Stevens Listing One // ------ serial.h #ifndef SERIAL_H #define SERIAL_H const int maxports = 2; // ------------ timer macros #define timed_out(timer) (timer==0) #define set_timer(timer, secs) timer=(secs)*182/10+1 #define disable_timer(timer) timer = -1 #define timer_running(timer) (timer > 0) #define countdown(timer) --timer #define timer_disabled(timer) (timer == -1) #define TIMER 8 // ----------- won't need this next compiler version typedef int bool; const bool false = 0; const bool true = !false; // ------- 8259 Programmable Interrupt Controllers const int PIC01 = 0x21; const int PIC00 = 0x20; const int EOI = 0x20; // End of Interrupt command // --------------- line status register values const int TBE = 0x20; // ------------ modem control register values const int DTR = 1; const int RTS = 2; const int OUT2 = 8; // ------------ modem status register values const int RLSD = 0x80; const int DSR = 0x20; const int CTS = 0x10; // ----------- interrupt enable register signals const int DataReady = 1; // ----- serial port initialization parameter byte union portinit { struct { unsigned wordlen : 2; unsigned stopbits : 1; unsigned parity : 3; unsigned brk : 1; unsigned divlatch : 1; } initbits; char initchar; }; // ---- parameters for serial I/O struct CommParameters { int parity; // 0, 1, 2 = none, odd, even int stopbits; // 1 or 2 int databits; // 7 or 8 int baud; // 300, 600, etc. bool handshake; // false = none, true = DSR/DTR int buffersize; // size of input buffer int timeout; // DTR timeout }; // ---- the CommPort class class CommPort { int portno; portinit initcom; static char* mp_recvbuff; static char* mp_nextin; static char* mp_nextout; static int buffersize; static int buffercount; static int refcount; CommParameters& commparms; CommPort* OutPort; static void interrupt newtimer(...); static void interrupt (*oldtimer)(...); static int serialtimer; int BasePort() { return (0x3f8-((portno)<<8)); } int TxData() { return BasePort(); } int RxData() { return BasePort(); } int DivLSB() { return BasePort(); } int DivMSB() { return BasePort()+1; } int IntEnable() { return BasePort()+1; } int IntIdent() { return BasePort()+2; } int LineCtl() { return BasePort()+3; } int ModemCtl() { return BasePort()+4; } int LineStatus() { return BasePort()+5; } int ModemStatus() { return BasePort()+6; } int irq() { return 4-(portno); } int vector() { return 12-(portno); } int ComIRQ() { return ~(1 << irq()); } void CommInterrupt(); void Initialize(); static CommPort* mp_CommPort[maxports]; static void interrupt (*oldcomint[maxports])(...); static void interrupt newcomint1(...); static void interrupt newcomint2(...); static void interrupt (*newcomint[maxports])(...); public: CommPort(int port, CommParameters& cp); ~CommPort(); void Register(CommPort* pcom) { OutPort = pcom; } static char* nextchar(); }; #endif Listing Two // --------- serial.cpp #include #include "serial.h" void interrupt (*CommPort::oldtimer)(...); int CommPort::serialtimer = -1; // ---- interrupt vector chain and for restoring vector on exit void interrupt (*CommPort::oldcomint[maxports])(...); // ---- pointers to CommPort objects CommPort *CommPort::mp_CommPort[maxports]; // ---- input capture buffer char* CommPort::mp_recvbuff; char* CommPort::mp_nextin; char* CommPort::mp_nextout; int CommPort::buffersize; int CommPort::buffercount; int CommPort::refcount; // ------ ISRs to count timer ticks void interrupt CommPort::newtimer(...) { (*oldtimer)(); if (timer_running(serialtimer)) countdown(serialtimer); } // ---- serial input interrupt service routines void interrupt CommPort::newcomint1(...) { mp_CommPort[0]->CommInterrupt(); } void interrupt CommPort::newcomint2(...) { mp_CommPort[1]->CommInterrupt(); } // --- table of ISR addresses void interrupt (*CommPort::newcomint[maxports])(...) = { CommPort::newcomint1, CommPort::newcomint2 }; // --- the object-based serial ISR void CommPort::CommInterrupt() { int c; bool timedout = false; // ---- send end of interrupt to the 8259 outp(PIC00,EOI); if (mp_recvbuff != 0) { if (mp_nextin == mp_recvbuff+buffersize) mp_nextin = mp_recvbuff; // circular buffer c = inp(RxData()); // read the input // --- pass the input to the other port if (OutPort != 0) { if (commparms.handshake) { // --- test for hardware handshake int ms = OutPort->ModemStatus(); if ((inp(ms) & DSR) == 0) { // --- other port has turned off DSR int mc = inp(ModemCtl()); // --- turn off DTR for this port outp(ModemCtl(), mc & ~DTR); // --- wait set_timer(serialtimer, 1); //commparms.timeout); enable(); while ((inp(ms) & DSR) == 0) { if (timed_out(serialtimer)) { timedout = true; // timeout break; } } disable(); // --- turn DTR back on for this port outp(ModemCtl(), mc | DTR); } } if (!timedout) { set_timer(serialtimer, 2); int ls = OutPort->LineStatus(); // ---- wait for transmitter buffer empty, other port enable(); while ((inp(ls) & TBE) == 0) { if (timed_out(serialtimer)) { timedout = true; break; } } disable(); if (!timedout) { // --- pass the received byte to the other port outp(OutPort->TxData(), (char)c); disable_timer(serialtimer); } } } if (buffercount == buffersize/2) { // ---- post the buffer overrun so dscope can report it *mp_nextin = (portno+1) | 0x80; *(mp_nextin+1) = 1; } else if (timedout) { // --- post the timeout so dscope can report it *mp_nextin++ = (portno+1) | 0x80; *mp_nextin++ = 2; buffercount++; } else { *mp_nextin++ = portno+1; // put port in buffer for display *mp_nextin++ = (char) c; // put char in buffer for display buffercount++; } } } // ---- construct a CommPort object CommPort::CommPort(int port, CommParameters& cp) : commparms(cp) { portno = port-1; buffersize = commparms.buffersize; // ensure that the buffer size is an even number buffersize++; buffersize &= 0xfffe; OutPort = 0; oldcomint[portno] = 0; if (portno >= 0 && portno < maxports) { // --- set the address of this object for the ISR to use mp_CommPort[portno] = this; if (refcount == 0) mp_recvbuff = new char[buffersize]; } // --- initialize the input buffer if (refcount == 0) { mp_nextin = mp_nextout = mp_recvbuff; buffercount = 0; } // --- reference counter refcount++; Initialize(); } // ---- destroy a CommPort object CommPort::~CommPort() { if (OutPort != 0) OutPort->Register(0); // de-register with the other port // --- is this the last of the CommPort objects to be destroyed? if (--refcount == 0) { // --- yes, delete the capture buffer and initialize its pointers delete mp_recvbuff; mp_recvbuff = 0; mp_nextin = mp_nextout = 0; // --- unhook the timer interrupt if (oldtimer != 0) { setvect(TIMER, oldtimer); oldtimer = 0; } } // --- unhook the serial interrupt if (oldcomint[portno] != 0) { setvect(vector(), oldcomint[portno]); oldcomint[portno] = 0; } } // -------- initialize the com port void CommPort::Initialize() { // --- build the line control byte initcom.initbits.parity = commparms.parity == 2 ? 3 : commparms.parity; initcom.initbits.stopbits = commparms.stopbits-1; initcom.initbits.wordlen = commparms.databits-5; initcom.initbits.brk = 0; initcom.initbits.divlatch = 1; outp(LineCtl(), initcom.initchar); // --- program the baud rate outp(DivLSB(), (char) ((115200L/commparms.baud) & 255)); outp(DivMSB(), (char) ((115200L/commparms.baud) >> 8)); initcom.initbits.divlatch = 0; outp(LineCtl(), initcom.initchar); // ---- intercept the timer interrupt vector if (oldtimer == 0) { oldtimer = getvect(TIMER); setvect(TIMER, newtimer); } // ------ hook serial interrupt vector if (oldcomint[portno] == 0) { oldcomint[portno] = getvect(vector()); setvect(vector(), newcomint[portno]); } // --- program the modem control register outp(ModemCtl(), (inp(ModemCtl()) | DTR | RTS | OUT2)); // --- program the IRQ into the 8259 outp(PIC01, (inp(PIC01) & ComIRQ())); // --- enable interrupts outp(IntEnable(), DataReady); // --- end of interrupt outp(PIC00, EOI); // ----- flush any old interrupts inp(RxData()); inp(IntIdent()); inp(LineStatus()); inp(ModemStatus()); } // ---- fetch the next port and character from the capture buffer char* CommPort::nextchar() { if (buffercount == 0) return 0; if (mp_nextout == mp_recvbuff+buffersize) mp_nextout = mp_recvbuff; mp_nextout += 2; --buffercount; return mp_nextout-2; }