resnicks@netcom.COM (Steve Resnick) (06/28/91)
Due to the number of requests for serial code, I am posting this driver I wrote. It's for Turbo/Borland C. Happy Hacking .... Steve #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 06/05/1991 15:24 UTC by resnicks@netcom # Source directory /u9/resnicks/sources/pc/asynch # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 11768 -rw-r--r-- asynch.c # 3229 -rw-r--r-- asynch.h # 2692 -rw-r--r-- circleq.c # 336 -rw-r--r-- circleq.h # 838 -rw-r--r-- main.c # 270 -rw-r--r-- makefile # # ============= asynch.c ============== if test -f 'asynch.c' -a X"$1" != X"-c"; then echo 'x - skipping asynch.c (File already exists)' else echo 'x - extracting asynch.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'asynch.c' && /* X * ASYCNCH.C A simple, low-level, interrupt driven comm driver for X * Turbo/Borland C. No assembly required! X * X * (C) Copyright 1991 Steve Resnick, Asylum Software. All Rights Reserved. X * X * Steve Resnick - 530 Lawrence Expressway, Box 374, Sunnyvale, Ca 94086 */ #include <stdio.h> #include <dos.h> #include "asynch.h" /* X * This table is used to translate a BPS rate to a baud rate divisor for the X * UART */ struct _baud { X word Rate, Divisor; } BaudTable[] = { X 110,1040,150,768,300,384,600,192,1200,96,2400,48,4800,24,9600,12, X 19200,6, /* I have no idea if this works */ } ; #if DEBUG byte PICVals[2][2]; /* Mirror PIC values to these arrays */ byte DataTable[2][20]; /* Mirror UART values to these arrays */ byte portinb(word port); /* Use our own port I/O functions */ void portoutb(word port, byte data); #else #define portinb(p) inportb(p) #define portoutb(p,b) outportb(p,b) #endif uart cports[4] = {COM1_DEFAULTS,COM2_DEFAULTS}; X /* X * SetPortParms sets up the UART line parameters and baud rate. X * If Base, IRQ, and BreakLength are specified as 0, defaults are used. X * (See ASYNCH.H) X * This may only be called for an open port. */ int SetPortParms(int PortNo, int Baud, int Parity, int Data, int Stop, X int Base, int IRQ, int BreakLength) { X int i, b; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X X if (Base) X cports[PortNo].addr = Base; X if (IRQ) X cports[PortNo].irqline = IRQ; X if (BreakLength) X cports[PortNo].break_length = BreakLength; X X cports[PortNo].parms = 0; X cports[PortNo].parms |= Data; X cports[PortNo].parms |= Stop; X cports[PortNo].parms |= Parity; X if (Baud != 0) X { X for (i = 0, b = -1; i < dim(BaudTable); i++) X if (BaudTable[i].Rate == Baud) X b = i; X if (b == -1) X return EINVBAUDRATE; X X cports[PortNo].baud = BaudTable[b].Divisor; X InitBaud(PortNo); X } X else if (cports[PortNo].baud == 0) X return EINVBAUDRATE; X portoutb(cports[PortNo].addr+LCR_REG,cports[PortNo].parms); X return 0; } /* X * copen opens a comm port spcified by PortNo. Input and Output buffers are X * allocated according to the BufSiz parameter. X * If Base, IRQ, and BreakLength are specified as 0, defaults are used. X * (See ASYNCH.H) X * X * When the port is opened, the following takes place: X * Buffer memory is allocated X * CommPort options are set X * The interrupt vector is set based on IRQ+8 X * The 8259 is enabled to recieve interrupts. X * The 8250 IE mask is set X * The 8250 OUT2 is asserted, enabling the interrupt line from the UART to the X * PIC. */ int copen(int PortNo, int BufSiz, int Baud, int Parity, int Data, int Stop, X int Base, int IRQ, int BreakLength) { X uart * Cp; X int Rc; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED) X return EPORTALREADYOPEN; X Cp = &cports[PortNo]; X if ((Cp->InBuffer = Qalloc(BufSiz)) == NULL) X return EMEMALLOC; X if ((Cp->OutBuffer = Qalloc(BufSiz)) == NULL) X { X Qfree(Cp->InBuffer); X return EMEMALLOC; X } X Cp->status |= PS_ENABLED; X Rc = SetPortParms(PortNo,Baud,Parity,Data,Stop,Base,IRQ,BreakLength); X if (Rc) X { X Qfree(Cp->InBuffer); X Qfree(Cp->OutBuffer); X Cp->status = 0; X return Rc; X } X SetIntVector(PortNo); X disable(); X SetPIC(PortNo,1); X SetUARTIe(PortNo); X enable(); X ControlDTR(PortNo,ON); X return 0; } /* X * cclose closes a serial port by disabling interrupts on the UART, then on X * the PIC and finally, returns the original vector to the IVT. */ int cclose(int PortNo) { X uart * Cp; X int Rc; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X Cp = &cports[PortNo]; X disable(); X ClrIntVector(PortNo); X SetPIC(PortNo,0); X enable(); X Cp->status = 0; X Qfree(Cp->InBuffer); X Qfree(Cp->OutBuffer); X portoutb(Cp->addr + IE_REG,0); X portoutb(Cp->addr + MCR_REG,1); X return 0; } /* X * InitBaud sets the baudrate on the UART by setting the divisor latch access X * bit (DLAB) then writing the divisor's low byte,then the divisors high byte. X * For baudrates >= 600 BPS, the high byte should be 0. (See ASYNCH.H) */ int InitBaud(int PortNo) { X uart * Cp; X byte bdata; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X Cp = &cports[PortNo]; X bdata = portinb(Cp->addr + LCR_REG); X portoutb(Cp->addr + LCR_REG,bdata | DLAB); X portoutb(Cp->addr + DIV_LOW,LOBYTE(Cp->baud)); X portoutb(Cp->addr + DIV_HI, HIBYTE(Cp->baud)); X portoutb(Cp->addr + LCR_REG,Cp->parms); X return 0; } /* X * SetPIC sets or clears the interrupt enable mask on the PIC for a X * particular interrupt. This function will work for both the master X * and the slave 8259A's in an AT. */ SetPIC(int PortNo, int State) { static int States[] = {-1,-1,-1,-1}; X int PICAddr = PIC; X byte mask; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if(State == 0 && States[PortNo] == -1) X return -1; X if (cports[PortNo].irqline > 7) X { X mask = ( 1 << (cports[PortNo].irqline - 8)); X PICAddr = 0xA0; X } X else X mask = (1 << cports[PortNo].irqline); X X if (State == 0) X { X States[PortNo] = portinb(PIC+1); X portoutb(PICAddr+1,(byte)States[PortNo]|mask); X return 0; X } X mask = ~mask; X States[PortNo] = portinb(PICAddr+1); X portoutb(PICAddr+1,States[PortNo] & mask); X portinb(PICAddr+1); X return 0; } /* X * EOI Sends a non-specific end of interrupt to the 8259A specific to the X * IRQ line of a com port */ int EOI(int PortNo) { X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X if (cports[PortNo].irqline > 7) X portoutb(0xA0,0x20); X else X portoutb(0x20,0x20); X return 0; } /* X * This is the interrupt handler for ALL comm ports. Each ISR for a comm X * port calls this routine passing the index into the UART definition array X * so that values may be extracted for things like UART address, etc. X * X * This is a re-entrant function, although interrupts are disabled to ensure X * that interrupts are handled one at a time for each UART. This may be X * over-kill and may need to be changed in the future. */ void IsrHandler(int PortNo) { X uart * Cp; X byte Idv, IIDvalue=0; X disable(); X Cp = &cports[PortNo]; X IIDvalue = portinb(Cp->addr+ IID_REG); X if ((IIDvalue & IID_PENDING) == 0) X { X IIDvalue &= IID_MASK ; X Idv = IIDvalue >> 1; X if (Idv & IID_DATA) X QinsertChar(Cp->InBuffer,portinb(Cp->addr)); X X if (Idv & IID_TEMPTY) X DrainOutQueue(PortNo); X X if (Idv & IID_LSTAT) X Cp->lstat = portinb(Cp->addr+ LSR_REG); X X if (Idv & IID_MSTAT) X Cp->mstat = portinb(Cp->addr+ MSR_REG); X X portinb(Cp->addr+5); X portinb(Cp->addr+6); X } X enable(); X SetUartOUT2(PortNo); X EOI(PortNo); } /* X * Dummy ISR's X * Each comm port needs to have it's own ISR, but we need only one real X * handler. Since an ISR shared my multiple interrupts cannot determine X * it's source, we set up these dummy ISR's to call IsrHandler specifying X * the port which needs service. */ void interrupt com1isr() { X IsrHandler(0); } void interrupt com2isr() { X IsrHandler(1); } void interrupt com3isr() { X IsrHandler(2); } void interrupt com4isr() { X IsrHandler(3); } /* X * SetIntVector sets the appropriate interrupt vector (correctly for both X * (master and slaved IRQ's) based on the IRQ. The original vector is saved X * in the UART structure so that it may be reset upon termination. */ SetIntVector(int PortNo) { X int NewInt; static void interrupt (*isrs[])() = {com1isr,com2isr,com3isr,com4isr}; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X cports[PortNo].oldisr = getvect(IRQINT(cports[PortNo].irqline)); X NewInt = IRQINT(cports[PortNo].irqline); X setvect(NewInt,isrs[PortNo]); X return 0; } /* X * ClrIntVector revectors interrupts back to the original vector before we X * loaded up. */ ClrIntVector(int PortNo) { X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X setvect(IRQINT(cports[PortNo].irqline),cports[PortNo].oldisr); X return 0; } /* X * SetUARTIe sets the interrupt enable mask on the UART. X * SetUartOUT2 is then called to unmask interrupts from the UART to the PIC */ SetUARTIe(int PortNo) { X int i; X word IntFlags = IIV_LSTAT | IIV_RDATA | IIV_MSTAT | IIV_XMITE; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X for (i = 5; i < 8; i++) X inportb(cports[PortNo].addr+i); X X portoutb(cports[PortNo].addr + IE_REG, IntFlags); X SetUartOUT2(PortNo); X return 0; } /* X * SetUARTOut2 set's the OUT2 line on the UART high, allowing interrupts X * to pass from the UART to the PIC (thanks, IBM) */ SetUartOUT2(int PortNo) { X byte IoData; X IoData = portinb(cports[PortNo].addr + MCR_REG); X portoutb(cports[PortNo].addr + MCR_REG,IoData | UARTIEMASK); X return 0; } /* X * DrainOutQueue reads characters from the output queue to the UART, as X * long as the UART is ready for data. If it is not ready, we return X * immediately. */ DrainOutQueue(int PortNo) { X byte IoData; X char Ch; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X X while(1) X { X IoData = portinb(cports[PortNo].addr + LSR_REG); X if (IoData & 0x20 || IoData & 0x40) X { X Ch = QreadChar(cports[PortNo].OutBuffer); X if (Ch == -1) X break ; X portoutb(cports[PortNo].addr,Ch); X continue ; X } X break ; X } X return 0; } /***************************************************************************** X ****************************** User Functions ******************************/ /* X * cgetc reads a character from the input queue and returns it, or -1 if no X * character is available. */ cgetc(int PortNo) { X if (PortNo > 3 || PortNo < 0) X return -10 - EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return -10 - EPORTNOTOPEN; X return QreadChar(cports[PortNo].InBuffer); } /* X * cputc writes a character to the output queue */ cputc(int PortNo, char Ch) { X if (PortNo > 3 || PortNo < 0) X return -10 - EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return -10 - EPORTNOTOPEN; X X QinsertChar(cports[PortNo].OutBuffer,Ch); X DrainOutQueue(PortNo); X return 0; } /* X * SendBreak sets a break condition on the serial line and holds it X * for break_lenght miliseconds */ int SendBreak(int PortNo) { X byte IoData; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X IoData = portinb(cports[PortNo].addr + LCR_REG); X portoutb(cports[PortNo].addr + LCR_REG,IoData | 0x40); X delay(cports[PortNo].break_length); X portoutb(cports[PortNo].addr + LCR_REG,IoData); X return 0; } /* X * ControlDTR raises or lowers DTR on a specific port */ int ControlDTR(int PortNo, int State) { X byte IoData; X if (PortNo > 3 || PortNo < 0) X return EINVPORT; X if (cports[PortNo].status & PS_ENABLED == 0) X return EPORTNOTOPEN; X IoData = portinb(cports[PortNo].addr + MCR_REG); X if (State == ON) X portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData | 1)); X else X portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData & ~1)&0xFF); X return 0; } /****************************************************************************** ****************************** Debugging Functions ***************************/ #if DEBUG void portoutb(word port, byte data) { X int Idx = port - 0x3f8; X if (Idx > -1 && Idx < 20) X DataTable[0][Idx] = data; X if (port >0x1F && port < 0x22) X { X Idx = port - 0x20; X PICVals[0][Idx] = data; X } X outportb(port,data); } byte portinb(word port) { X int Idx = port - 0x3F8; X byte Data; X Data = inportb(port); X if (port >0x1F && port < 0x22) X { X Idx = port - 0x20; X PICVals[1][Idx] = Data; X } X else if (Idx > -1 && Idx < 20) X DataTable[1][Idx] = Data; X return Data; } #endif X X SHAR_EOF chmod 0644 asynch.c || echo 'restore of asynch.c failed' Wc_c="`wc -c < 'asynch.c'`" test 11768 -eq "$Wc_c" || echo 'asynch.c: original size 11768, current size' "$Wc_c" fi # ============= asynch.h ============== if test -f 'asynch.h' -a X"$1" != X"-c"; then echo 'x - skipping asynch.h (File already exists)' else echo 'x - extracting asynch.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'asynch.h' && #if !defined(ASYNCH_INCLUDED) /* Prevent multiple includes */ #define ASYNCH_INCLUDED #include "circleq.h" typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword; typedef struct _lcr { X word databits : 2; X word stopbits : 1; X word parity : 2; X word stuckparity : 1; X word breakenable : 1; X word dlab : 1; } lcr; typedef struct _uart { X word addr; X byte irqline; X word baud; X byte lstat; X X byte mstat; X byte status; X byte break_length; X byte parms; X CircleQ *InBuffer; X CircleQ *OutBuffer; X void interrupt (*oldisr)(); } uart; #define LineStatus(n) ((n >= 0 && n <4) ? cports[n].lstat : EINVPORT) #define ModemStatus(n) ((n >= 0 && n <4) ? cports[n].mstat : EINVPORT) X extern uart cports[4]; #define COM1_DEFAULTS\ X {0x3F8,4,0,0,0,0,250,\ X DATA8|STOP1|NONE,\ X NULL,NULL} X #define COM2_DEFAULTS \ X {0x2F8,3,0,0,0,0,250,\ X DATA8|STOP1|NONE,\ X NULL,NULL} X #define PIC 0x20 /* 8259A PIC Address */ #define IRQINT(n) ((n < 9) ? n+8 : (n-9)+0x70) X /* Make int value from IRQ */ #define IID(n) (n & 7) /* Get Interrupt ID from reg val */ /* X * Define Interrupt ID values from the interrupt ID register */ #define IID_PENDING 0x01 #define IID_LSTAT 0x00 /* Line status change */ #define IID_DATA 0x02 /* Data is available from 8250 */ #define IID_TEMPTY 0x04 /* Transmitter buffer empty */ #define IID_MSTAT 0x06 /* New modem status */ #define IID_MASK 0x07 X #define IIV_LSTAT 0x01 #define IIV_XMITE 0x02 #define IIV_RDATA 0x04 #define IIV_MSTAT 0x08 /* X * Define some offsets from the UART base address for various registers */ #define XMT_REG 0x00 /* Transmitter holding buffer */ #define RCV_REG 0x00 /* Reciever holding buffer */ #define DIV_LOW 0x00 /* Divisor low byte when DLAB=1 */ #define DIV_HI 0x01 /* Devisor high byte when DLAB=1 */ X #define IE_REG 0x01 /* Interrupt enable register */ #define IID_REG 0x02 /* Interrupt ID register */ #define LCR_REG 0x03 /* line control register */ #define MCR_REG 0x04 /* Modem control register */ #define LSR_REG 0x05 /* line status register */ #define MSR_REG 0x06 /* modem status register */ X /* X * Stuff for the line control register */ #define NONE 0x00 /* No Parity */ #define ODD 0x08 /* Odd Parity */ #define EVEN 0x18 /* Even Parity */ #define STOP1 0x00 /* Need I explain these? */ #define STOP2 0x04 #define DATA5 0x00 #define DATA6 0x01 #define DATA7 0x02 #define DATA8 0x03 #define DLAB 0x80 X #define UARTIEMASK 0x08 /* Mask used to enable interrupts on X the 8250 OUT2 line */ #define PS_ENABLED 0x01 #define PS_INTERRUPT 0x02 /* Not used */ X enum errors {EPORTNOTOPEN=1,EPORTALREADYOPEN,EINVBAUDRATE,EINVPORT,EMEMALLOC}; #define COM1 0 #define COM2 1 #define ON 1 #define OFF 0 #define HIBYTE(n) ((n >> 8) & 0xFF) #define LOBYTE(n) (n & 0xFF) X #if !defined(dim) #define dim(x) (sizeof(x)/sizeof(x[0])) #endif int SetPortParms(int PortNo, int Baud, int Parity, int Data, int Stop, X int Base, int IRQ, int BreakLength); int copen(int PortNo, int BufSiz, int Baud, int Parity, int Data, int Stop, X int Base, int IRQ, int BreakLength); int cclose(int PortNo); int InitBaud(int PortNo); SetPIC(int PortNo, int State); cgetc(int PortNo); cputc(int PortNo, char Ch); X #endif X SHAR_EOF chmod 0644 asynch.h || echo 'restore of asynch.h failed' Wc_c="`wc -c < 'asynch.h'`" test 3229 -eq "$Wc_c" || echo 'asynch.h: original size 3229, current size' "$Wc_c" fi # ============= circleq.c ============== if test -f 'circleq.c' -a X"$1" != X"-c"; then echo 'x - skipping circleq.c (File already exists)' else echo 'x - extracting circleq.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'circleq.c' && #include <stdio.h> #include <stdlib.h> #include <errno.h> #include "circleq.h" X /* X * Qalloc allocates space for a Queue Control Structure (CircleQ) and X * a buffer to be contained within. X * If space is not available for this structure, or it's buffer, NULL X * is returned, otherwise a pointer to a CirclQ is returned. */ CircleQ * Qalloc(int Size) { X CircleQ * Qptr; X if ((Qptr = calloc(sizeof(CircleQ),1)) == NULL) X { X errno = ENOMEM; X return NULL; X } X if ((Qptr->Buffer = calloc(Size,1)) == NULL) X { X free(Qptr); X errno = ENOMEM; X return NULL ; X } X Qptr->Head = Qptr->Tail = Qptr->Buffer; X Qptr->Size = Size; X return Qptr; } /* X * QinsertChar inserts a character on a queue. The tail pointer is used X * to indicate where the character shall be placed. Once a character is X * written, the tail pointer is updated. If the tail pointer overruns the X * head pointer, the overrun flag is set. Once the overrun flag is set, the X * head pointer will be updated on all subsequent writes to ensure that X * the buffer always contains Qptr->Size characters. If Qptr->Tail or X * Qptr->Head reach the end of the buffer, they are wrapped around to X * the beginning of the buffer. */ void QinsertChar(CircleQ * Qptr, char Ch) { X *Qptr->Tail = Ch; X if ((Qptr->Tail + 1) >= (Qptr->Buffer+Qptr->Size)) X Qptr->Tail = Qptr->Buffer; X else X Qptr->Tail ++; X X if (Qptr->Head == Qptr->Tail) X Qptr->Flag = 1; /* Queue buffer overflow */ X X if (Qptr->Flag) X Qptr->Head ++ ; X if (Qptr->Head >= Qptr->Buffer + Qptr->Size) X Qptr->Head = Qptr->Buffer; } /* X * QpeekChar returns a character from the specified queue without removing X * it. If the queue is empty -1 is returned. */ char QpeekChar(CircleQ * Qptr) { X if (Qptr->Head == Qptr->Tail) X { X Qptr->Flag = 0; X return -1; X } X return *Qptr->Head; } /* X * QreadChar returns a character from the specified queue. The queue is then X * updated to reflect the last read. If the queue is empty, -1 is returned. */ char QreadChar(CircleQ * Qptr) { X char Ch = -1; X if (Qptr->Head == Qptr->Tail) X { X Qptr->Flag = 0; X return -1; X } X Ch = *Qptr->Head; X Qptr->Head ++ ; X if (Qptr->Head >= Qptr->Buffer + Qptr->Size) X Qptr->Head = Qptr->Buffer; X return Ch; } /* X * Qfree frees a queue control structure (CircleQ) and all buffers X * associated with it (Qptr->Buffer). This Head and Tail pointers are X * set to NULL, and Size and Flag (overrun flag) are set to 0. X * Qptr is then freed, releasing all memory associated with the queue. X * No subsequent operations may be made on this queue. */ void Qfree(CircleQ * Qptr) { X if (Qptr != NULL) X { X free(Qptr->Buffer); X Qptr->Buffer = Qptr->Head = Qptr->Tail = NULL; X Qptr->Size = Qptr->Flag = 0; X free(Qptr); X } } X SHAR_EOF chmod 0644 circleq.c || echo 'restore of circleq.c failed' Wc_c="`wc -c < 'circleq.c'`" test 2692 -eq "$Wc_c" || echo 'circleq.c: original size 2692, current size' "$Wc_c" fi # ============= circleq.h ============== if test -f 'circleq.h' -a X"$1" != X"-c"; then echo 'x - skipping circleq.h (File already exists)' else echo 'x - extracting circleq.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'circleq.h' && #if !defined(CIRCLEQ_INCLUDE) #define CIRCLEQ_INCLUDE typedef struct _circq { X char * Head; X char * Tail; X char * Buffer; X int Size; X int Flag; } CircleQ; X CircleQ * Qalloc(int size); void Qfree(CircleQ * qptr); void QinsertChar(CircleQ * qptr, char ch); char QpeekChar(CircleQ * qptr); char QreadChar(CircleQ * qptr); #endif X SHAR_EOF chmod 0644 circleq.h || echo 'restore of circleq.h failed' Wc_c="`wc -c < 'circleq.h'`" test 336 -eq "$Wc_c" || echo 'circleq.h: original size 336, current size' "$Wc_c" fi # ============= main.c ============== if test -f 'main.c' -a X"$1" != X"-c"; then echo 'x - skipping main.c (File already exists)' else echo 'x - extracting main.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'main.c' && #include <stdio.h> #include <conio.h> #include "asynch.h" void Beep(void); int ctrlc(void) { X cputc(COM2,3); X return 1; } main() { X int RetCode, c; X if((RetCode = copen(COM2,2048,2400,NONE,DATA8,STOP1,0,0,0)) != 0) X { X printf("copen failed: Code: %d\n",RetCode); X return -1; X } X clrscr(); X ctrlbrk(ctrlc); X fclose(stdout); X while(1) X { X if (kbhit()) X { X c = getch(); X if (c == 0) X { X switch(getch()&0xFF) X { X case 68 : X cclose(COM2); X return 0; X case 35 : X ControlDTR(COM2,OFF); X delay(500); X ControlDTR(COM2,ON); X break ; X case 48 : X SendBreak(COM2); X break ; X default : X Beep(); X break; X } X X } X else X cputc(COM2,c); X } X if ((c = cgetc(COM2)) >= 0) X fprintf(stderr,"%c",c); X } } void Beep() { X sound(1000); X delay(250); X nosound(); } X X SHAR_EOF chmod 0644 main.c || echo 'restore of main.c failed' Wc_c="`wc -c < 'main.c'`" test 838 -eq "$Wc_c" || echo 'main.c: original size 838, current size' "$Wc_c" fi # ============= makefile ============== if test -f 'makefile' -a X"$1" != X"-c"; then echo 'x - skipping makefile (File already exists)' else echo 'x - extracting makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'makefile' && X !if $d(DEBUG) objs=asynch.obj circleq.obj test.obj cargs=-ms -v -DDEBUG=1 X test.exe: $(objs) X cc $(cargs) -etest.exe $(objs) !else objs=asynch.obj circleq.obj main.obj cargs=-ms -v X term.exe: $(objs) X cc $(cargs) -eterm.exe $(objs) !endif X .c.obj: X cc -c $(cargs) $< X X SHAR_EOF chmod 0644 makefile || echo 'restore of makefile failed' Wc_c="`wc -c < 'makefile'`" test 270 -eq "$Wc_c" || echo 'makefile: original size 270, current size' "$Wc_c" fi exit 0 -- ------------------------------------------------------------------------------- resnicks@netcom.com, steve@camphq, IFNA: 1:143/105.0, co moderator for comp.binaries.os2 Real life: Steve Resnick. Chief Software Architect, Process Scientific, Inc Flames, grammar and spelling errors >/dev/null The Asylum OS/2 BBS - (408)263-8017 12/2400,8,1 - Running Maximus CBCS 1.2 -------------------------------------------------------------------------------