/* RS-232.C contains code written by Bill Bytheway - AA6ED * that allows the developer to write a communications * interface for controlling up to 8 COM ports at the same * time using IRQ 1-15. It is written for reuse and is the * basis for many communications applications written by the * author. * * It has a 'main' section used to test the echo of data * from one port to another, along with keyboard access. * * Permission granted for non-commercial copying and use, * provided this notice is retained. Copyright 1999 * William H. Bytheway AA6ED, All Rights Reserved. */ #include #include #include /* Define the communications port absolute numbers */ #define COM1 0 #define COM2 1 #define COM3 2 #define COM4 3 #define COM5 4 #define COM6 5 #define COM7 6 #define COM8 7 #define buff_size 0xFF /* Define the interrupt mask structure for every IRQ interrupt */ struct { unsigned int port_address; unsigned char int_id; unsigned char base_is_set; unsigned char datardy; unsigned char ringbuf[buff_size+1]; unsigned int readptr; unsigned int writptr; } COM[8] = { { 0x3F8, 4, 0, 0, 0, 0, 0, }, /* COM 1 */ { 0x2F8, 3, 0, 0, 0, 0, 0, }, /* COM 2 */ { 0x3E8, 10, 0, 0, 0, 0, 0, }, /* COM 3 */ { 0x3E0, 11, 0, 0, 0, 0, 0, }, /* COM 4 */ { 0x2F0, 12, 0, 0, 0, 0, 0, }, /* COM 5 */ { 0x2E8, 15, 0, 0, 0, 0, 0, }, /* COM 6 */ { 0x2E0, 0, 0, 0, 0, 0, 0, }, /* COM 7 */ { 0x260, 0, 0, 0, 0, 0, 0, }}; /* COM 8 */ /* Define the IRQ interrupt addresses and masks */ struct { unsigned char int_id; unsigned char PIC1_mask; unsigned char PIC2_mask; unsigned char old_PIC1_mask; unsigned char old_PIC2_mask; } IRQ[16] = { { 0x08, 0xFE, 0xFF, 0, 0}, /* IRQ00 tick counter */ { 0x09, 0xFD, 0xFF, 0, 0}, /* IRQ01 Keyboard */ { 0x0A, 0xFB, 0xFF, 0, 0}, /* IRQ02 PIC2 */ { 0x0B, 0xF7, 0xFF, 0, 0}, /* IRQ03 COM2 */ { 0x0C, 0xEF, 0xFF, 0, 0}, /* IRQ04 COM1 */ { 0x0D, 0xDF, 0xFF, 0, 0}, /* IRQ05 Mouse */ { 0x0E, 0xBF, 0xFF, 0, 0}, /* IRQ06 Floppy Drive */ { 0x0F, 0x7F, 0xFF, 0, 0}, /* IRQ07 Printer */ { 0x70, 0xFB, 0xFE, 0, 0}, /* IRQ08 Clock */ { 0x71, 0xFB, 0xFD, 0, 0}, /* IRQ09 Redirect IRQ2*/ { 0x72, 0xFB, 0xFB, 0, 0}, /* IRQ10 COM3 */ { 0x73, 0xFB, 0xF7, 0, 0}, /* IRQ11 COM4 */ { 0x74, 0xFB, 0xEF, 0, 0}, /* IRQ12 COM5 */ { 0x75, 0xFB, 0xDF, 0, 0}, /* IRQ13 System Int */ { 0x76, 0xFB, 0xBF, 0, 0}, /* IRQ14 Fixed Disk */ { 0x77, 0xFB, 0x7F, 0, 0}}; /* IRQ15 COM6 */ /* Define address adders for the various Async card registers */ #define RBR 0x00 /* Transmitter Holding Register */ #define IER 0x01 /* Interrupt Enable Register */ #define IIR 0x02 /* Interrupt Identification Register */ #define LCR 0x03 /* Line Control Register */ #define MCR 0x04 /* Modem Control Register */ #define LSR 0x05 /* Line Status Register */ #define MSR 0x06 /* Modem Status Register */ #define DLL 0x00 /* Divisor Latch Least Significant */ #define DLM 0x01 /* Divisor Latch Most Significant */ /* Define port addresses for the 8259 interrupt controller */ #define PICCMD1 0x20 /* Primary Controller */ #define PICMSK1 0x21 #define PICCMD2 0xA0 /* Secondary Controller */ #define PICMSK2 0xA1 #define PICEOI 0x20 /* End of interrupt */ /* Define UART byte settings */ #define pSpace 0x38 #define pOdd 0x08 #define pMark 0x28 #define pEven 0x18 #define pNone 0x00 /* Define UART number of data bits */ #define d5 0x00 #define d6 0x01 #define d7 0x02 #define d8 0x03 /* define UART number of stop bits */ #define s1 0x00 #define s2 0x04 #define true 1 #define false 0 /* Store the pointers to the interrupt address */ void interrupt (*Com1)(); void interrupt (*Com2)(); void interrupt (*Com3)(); void interrupt (*Com4)(); void interrupt (*Com5)(); void interrupt (*Com6)(); void interrupt (*Com7)(); void interrupt (*Com8)(); /* Async Interrupt routine */ void Asynch_COM(int irq_id) { int baseid; for (baseid = 0; baseid <= 7; baseid++) { if ((COM[baseid].int_id == irq_id) && COM[baseid].base_is_set) { int ComBase = COM[baseid].port_address; if (inportb(ComBase+IIR) == 0x04) { COM[baseid].ringbuf[COM[baseid].writptr] = inportb(ComBase + RBR); COM[baseid].writptr = (COM[baseid].writptr + 1) & buff_size; COM[baseid].datardy = true; } if (irq_id > 7) outportb(PICCMD2, PICEOI); outportb(PICCMD1,PICEOI); } /* test for matching interrupt and base is set */ } /* loop through 8 COM ports and test for baseid */ } /* end of the interrupt at this point and return */ /* Pass the correct interrupt to the Async_COM(interrupt id) */ void interrupt IRQ0_Interrupt() { Asynch_COM(0); } void interrupt IRQ1_Interrupt() { Asynch_COM(1); } void interrupt IRQ2_Interrupt() { Asynch_COM(2); } void interrupt IRQ3_Interrupt() { Asynch_COM(3); } void interrupt IRQ4_Interrupt() { Asynch_COM(4); } void interrupt IRQ5_Interrupt() { Asynch_COM(5); } void interrupt IRQ6_Interrupt() { Asynch_COM(6); } void interrupt IRQ7_Interrupt() { Asynch_COM(7); } void interrupt IRQ8_Interrupt() { Asynch_COM(8); } void interrupt IRQ9_Interrupt() { Asynch_COM(9); } void interrupt IRQ10_Interrupt() { Asynch_COM(10); } void interrupt IRQ11_Interrupt() { Asynch_COM(11); } void interrupt IRQ12_Interrupt() { Asynch_COM(12); } void interrupt IRQ13_Interrupt() { Asynch_COM(13); } void interrupt IRQ14_Interrupt() { Asynch_COM(14); } void interrupt IRQ15_Interrupt() { Asynch_COM(15); } /* Open the COM port */ void OpenCom(int ComPort, int baud, int parity, int databits, int stopbits) { int rate; int LCRreg; int interrupt_id; int interrupt_no; int ComBase; COM[ComPort].base_is_set = true; interrupt_no = COM[ComPort].int_id; interrupt_id = IRQ[interrupt_no].int_id; switch (ComPort) { case 0: Com1 = getvect(interrupt_id); break; case 1: Com2 = getvect(interrupt_id); break; case 2: Com3 = getvect(interrupt_id); break; case 3: Com4 = getvect(interrupt_id); break; case 4: Com5 = getvect(interrupt_id); break; case 5: Com6 = getvect(interrupt_id); break; case 6: Com7 = getvect(interrupt_id); break; case 7: Com8 = getvect(interrupt_id); break; } disable(); switch (interrupt_no) { case 0: setvect(interrupt_id,IRQ0_Interrupt); break; case 1: setvect(interrupt_id,IRQ1_Interrupt); break; case 2: setvect(interrupt_id,IRQ2_Interrupt); break; case 3: setvect(interrupt_id,IRQ3_Interrupt); break; case 4: setvect(interrupt_id,IRQ4_Interrupt); break; case 5: setvect(interrupt_id,IRQ5_Interrupt); break; case 6: setvect(interrupt_id,IRQ6_Interrupt); break; case 7: setvect(interrupt_id,IRQ7_Interrupt); break; case 8: setvect(interrupt_id,IRQ8_Interrupt); break; case 9: setvect(interrupt_id,IRQ9_Interrupt); break; case 10: setvect(interrupt_id,IRQ10_Interrupt); break; case 11: setvect(interrupt_id,IRQ11_Interrupt); break; case 12: setvect(interrupt_id,IRQ12_Interrupt); break; case 13: setvect(interrupt_id,IRQ13_Interrupt); break; case 14: setvect(interrupt_id,IRQ14_Interrupt); break; case 15: setvect(interrupt_id,IRQ15_Interrupt); break; } IRQ[interrupt_no].old_PIC2_mask = inportb(PICMSK2); IRQ[interrupt_no].old_PIC1_mask = inportb(PICMSK1); outportb(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask & IRQ[interrupt_no].PIC2_mask)); outportb(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask & IRQ[interrupt_no].PIC1_mask)); enable(); outportb(PICCMD2,PICEOI); outportb(PICCMD1,PICEOI); ComBase = COM[ComPort].port_address; outportb(IER+ComBase, 0x01); outportb(MCR+ComBase, 0x0B); LCRreg = 0x80; LCRreg = LCRreg | parity; LCRreg = LCRreg | databits; LCRreg = LCRreg | stopbits; outportb(LCR+ComBase, LCRreg); rate = 3840 / (baud /30); outportb(DLM+ComBase, (rate/256 & 0xFF)); outportb(DLL+ComBase, (rate & 0xFF)); outportb(LCR+ComBase, LCRreg & 0x7F); /* final flush to clear both interrupt controllers */ Asynch_COM(COM[COMPORT].int_id); } /*------------ Close any initialized COM -----------------------*/ void CloseCom(int ComPort) { int interrupt_no, interrupt_id, ComBase; ComBase = COM[ComPort].port_address; COM[ComPort].base_is_set = false; interrupt_no = COM[ComPort].int_id; interrupt_id = IRQ[interrupt_no].int_id; outport(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask)); outport(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask)); switch (ComPort) { case 0: setvect(interrupt_id,Com1); break; case 1: setvect(interrupt_id,Com2); break; case 2: setvect(interrupt_id,Com3); break; case 3: setvect(interrupt_id,Com4); break; case 4: setvect(interrupt_id,Com5); break; case 5: setvect(interrupt_id,Com6); break; case 6: setvect(interrupt_id,Com7); break; case 7: setvect(interrupt_id,Com8); break; } outportb(IER+ComBase, 0x00); outportb(MCR+ComBase, 0x00); } /* Checks the datardy status for a given receive port and gets the char */ /* It returns a -1 if there isn't anything, or the character if there is */ int recvchar(int ComPort) { int ch = -1; if (COM[ComPort].datardy) { ch = COM[ComPort].ringbuf[COM[ComPort].readptr]; COM[ComPort].readptr = (COM[ComPort].readptr + 1) & buff_size; if (COM[ComPort].readptr == COM[ComPort].writptr) COM[ComPort].datardy = false; } return(ch); } /* Transmits a character to the appropriate communications port */ int xmitchar(int ComPort, int ch) { int LSRreg,i=-1; do { LSRreg = inportb(LSR+COM[ComPort].port_address) & 0x20; i++; } while (LSRreg == 0); outportb(RBR+COM[ComPort].port_address,ch); return(i); } /* Returns a -1 if there is no DCD connect, a +1 if there is */ int DCDconnect(int ComPort) { int ComBase; int i = -1; ComBase = COM[ComPort].port_address; /* Set ComBase Address */ if ((inportb(ComBase + MSR) & 0x80) == 0x80) i = 1; return (i); } /* Resets the input buffer on any given comport */ void purgeline(int ComPort) { COM[ComPort].readptr = COM[ComPort].writptr; } /* ----------------------------- MAIN PROGRAM ------------------ */ /* THE CODE BELOW SHOWS A TYPICAL EXAMPLE OF HOW THIS IS USED */ void main() { char ch = 0; char ch1 = 0; textmode(C4350); setcbrk(0); textcolor(WHITE); clrscr(); OpenCom(COM5,2400,pNone,d8,s1); OpenCom(COM2,4800,pNone,d8,s1); do { /* First COMM Port */ if (COM[COM5].datardy) { ch = recvchar(COM5) & 0x7F; cprintf("%c",ch); xmitchar(COM2,ch); } /* Second COMM Port */ if (COM[COM2].datardy) { ch = recvchar(COM2) & 0x7F; cprintf("%c",ch); xmitchar(COM5,ch); } /* Keyboard */ if (bioskey(1) !=0) { ch1 = bioskey(0); xmitchar(COM2,ch1); } } while (ch1 !=27); CloseCom(COM5); CloseCom(COM2); }