/* // COMDEMO.C // // Serial communications demo program for the Cport communications library. // // Copyright (c) 1993 Bri Productions // */ #include "cport.h" #include "xmodem.h" #include #include #include #include #include #include /* //------------------------------------- // // Microsoft portability // //------------------------------------- */ #if M_I86 #include #include #include #define BLUE 1 #define LIGHTGRAY 7 #define bioskey(a) _bios_keybrd(a) #define gotoxy(x,y) _settextposition((short)(y), (short)(x)) #define clrscr() _clearscreen(_GWINDOW) #define textattr(a) _settextcolor((short)((a)&0xf)); _setbkcolor((short)((a)>>4)) #define window(a,b,c,d) _settextwindow((short)(b),(short)(a),(short)(d),(short)(c)) #define cputs(s) _outtext(s) int wherex (void); int wherey (void); void delay (clock_t milliseconds); void clreol (void); #endif /* //------------------------------------- // // Queue sizes and thresholds // //------------------------------------- */ #define TXQ 4096 #define RCVQ 4096 #define THRESH (RCVQ * 3 / 4) /* //------------------------------------- // // numbers of parameters // //------------------------------------- */ #define NUM_COM 4 #define NUM_BAUD 9 #define NUM_MODE 2 #define NUM_HND 4 /* //------------------------------------- // // scan codes // //------------------------------------- */ #define A 0x1E00 #define B 0x3000 #define C 0x2E00 #define D 0x2000 #define E 0x1200 #define F 0x2100 #define G 0x2200 #define H 0x2300 #define I 0x1700 #define M 0x3200 #define N 0x3100 #define O 0x1800 #define P 0x1900 #define R 0x1300 #define S 0x1F00 #define W 0x1100 #define X 0x2D00 #define PGDN 0x5100 #define PGUP 0x4900 #define COM3A PORT2|IRQ5 /* //------------------------------------- // // text attributes // //------------------------------------- */ #define NORM 0x07 #define BOLD 0x08 #define FAINT 0xF7 #ifndef BLINK #define BLINK 0x80 #endif #define REVRS 0x77 #define ESC 0x1b /* //------------------------------------- // // parameter constants // //------------------------------------- */ const unsigned id[] = { COM1, COM2, COM3A, COM4 }; const int baud[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300 }; const byte mode[] = { W8|S1|NONE, W7|S1|EVEN }; const byte hndshk[] = { OFF ,SOFT, HARD, HARD|SOFT }; /* //------------------------------------- // // parameter indexes // //------------------------------------- */ struct indx{ byte id; byte baud; byte mode; byte ansi; byte hndshk; byte lf; byte echo; }indx = { 0, 3, 0, 1, 0, 0, 0 }; /* //------------------------------------- // // parameter messages // //------------------------------------- */ struct{ char *id [NUM_COM ]; char *baud [NUM_BAUD]; char *mode [NUM_MODE]; char *ansi [2]; char *hndshk[NUM_HND]; char *lf [2]; }msg={ { "COM1", "COM2", "COM3", "COM4" }, { "115k", "57600", "38400", "19200", "9600", "4800", "2400", "1200", "300" }, { "8-1-N", "7-1-E" }, { "TTY", "ANSI" }, { "NONE", "SOFT", "HARD", "BOTH" }, { " ", "LF" } }; /* //------------------------------------- // // screen coordinates // //------------------------------------- */ static int x = 1; /* cursor location */ static int y = 1; static byte attrib = NORM; /* present text attribute */ #define ERR_X 41 /* error message x coordinate */ #define STAT_X 60 /* status message x coordinate */ /* //------------------------------------- // // function prototypes // //------------------------------------- */ static void Init (void); static void Uninit (void); static void NewParam (void); static void Ansi (void); static void CheckError (void); static void CheckStatus (void); static void Upload (void); static void Download (void); static int callback (int msg, unsigned param); static void put_ch (char c); char *Xmsg[] = { "\r\ntransfer successful", "\r\nfile error", "\r\ntransfer canceled", "\r\nmemory error" }; struct C_param param; COM com; /* //------------------------------------- // // main() // //------------------------------------- */ void main(void) { char c; unsigned key; byte dtr = ON; /* initialize */ Init(); while(1) { /* Poll the keyboard buffer for available keystrokes. */ /* Meanwhile, the receive queue is checked for available */ /* characters, check for errors and check the modem status */ while(!bioskey(1)) { /* If a character(s) is available in the receive queue, */ /* fetch it. If it is and escape character, it must be */ /* check to see if it is the start of an ansi sequence. */ /* Otherwise the cursor position is updated, and the */ /* character is printed. */ if(ComLenRx(com)) { c = ComGetc(com); if(c == ESC && indx.ansi) { Ansi(); gotoxy(x, y); continue; } put_ch(c); } /* Check for errors and changes in the modem status */ CheckError(); CheckStatus(); } /* When a key is pressed, the key is fetched, and the */ /* keyboard flags are checked to see if the alt key was */ /* also pressed. If the alt key was pressed, we must */ /* check if the key is a valid command, and if so */ /* process it accordingly */ key = bioskey(0); if(!(key & 0x00ff)) { switch(key) { /* exit */ case X: Uninit(); /* Next com port */ case C: ComParam(com, ¶m); ComClose(com); do { indx.id++; indx.id %= NUM_COM; param.id = id[indx.id]; } while((com = ComOpenS(¶m)) == NULL); NewParam(); break; /* Next baud rate */ case B: indx.baud++; indx.baud %= NUM_BAUD; ComBaud(com, baud[indx.baud]); NewParam(); break; /* Next word length */ case M: indx.mode++; indx.mode %= NUM_MODE; ComMode(com, mode[indx.mode]); NewParam(); break; /* Toggle ansi terminal */ case A: indx.ansi ^= 1; NewParam(); break; /* Next handshake scheme */ case H: indx.hndshk++; indx.hndshk %= NUM_HND; ComHandshake(com, hndshk[indx.hndshk], THRESH); NewParam(); break; /* Echo */ case E: indx.echo ^= 1; break; /* Toggle LF append */ case N: indx.lf ^= 1; NewParam(); break; /* Initialize hayes modem */ case I: ComPuts(com, "ATZ\r\n"); break; /* Hayes modem dial command */ case D: ComPuts(com, "ATDT"); break; /* Hayes modem hang up command */ case G: ComPuts(com, "+++"); delay(3000); ComPuts(com, "ATH0\r\n"); break; case PGUP: Upload(); break; case PGDN: Download(); break; case R: dtr ^= 1; ComDtr(com, dtr); break; default: continue; } } /* If the key was not a command, put the character in the */ /* transmit queue. If the character is a carriage return, */ /* append a line feed to it just in case. */ else { ComPutc(com, (char)key); if(indx.echo) put_ch((char)key); CheckStatus(); CheckError(); } } } /* //------------------------------------- // // Modified putch() // //------------------------------------- */ void put_ch(char c) { gotoxy(x, y); putch(c); if(c == '\r' && indx.lf) putch('\n'); if(c == '\b') printf(" \b"); x = wherex(); y = wherey(); #ifdef M_I86 if(x == 1 && y == 25) _scrolltextwindow(1); #endif } /* //------------------------------------- // // Initialize // //------------------------------------- */ void Init(void) { FILE *fp; /* If an initialization file exists, load it. */ fp = fopen("comdemo.ini", "rb"); if(fp) fread(&indx, sizeof(struct indx), 1, fp); /* Set up the screen */ clrscr(); textattr(BLUE|(LIGHTGRAY<<4)); gotoxy(1,25); cprintf(" %4s ³%5s %s ³ %-4s ³ %-4s ³ %2s ³" " no errors ³ CTS= DSR= RI= DCD= ", msg.id[indx.id], msg.baud[indx.baud], msg.mode[indx.mode], msg.ansi[indx.ansi], msg.hndshk[indx.hndshk], msg.lf[indx.lf]); /* Initialize the serial port to the default parameters, */ /* set the timeout and set the DTR and RTS lines. */ com = ComOpen(id[indx.id], baud[indx.baud], mode[indx.mode], RCVQ, TXQ); /* restore the normal screen */ textattr(LIGHTGRAY); window(1,1,80,24); } /* //------------------------------------- // // terminate // //------------------------------------- */ void Uninit(void) { FILE *fp; /* Store the present parameters in an .ini file so the program */ /* will remember next time it is executed. */ fp = fopen("comdemo.ini", "wb"); if(fp) fwrite(&indx, sizeof(struct indx), 1, fp); ComClose(com); system("cls"); puts("\nÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸" "\n³ Cport v2.0 - Copyright (c) 1993 Bri Productions ³" "\nÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´" "\n³ Bri Productions, P.O. Box 7121, Fremont, CA 94537-7121, USA, (510) 794-0616 ³" "\nÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ;" ); exit(0); } /* //------------------------------------- // // new parameter // //------------------------------------- */ void NewParam(void) { /* Update the status line with the new parameters */ window(1,25, 80, 25); textattr(BLUE|(LIGHTGRAY<<4)); cprintf(" %4s ³%5s %s ³ %-4s ³ %-4s ³ %s ³", msg.id[indx.id], msg.baud[indx.baud], msg.mode[indx.mode], msg.ansi[indx.ansi], msg.hndshk[indx.hndshk], msg.lf[indx.lf]); /* restore the normal screen */ textattr(attrib); window(1,1,80,24); gotoxy(x, y); } /* //------------------------------------- // // ansi control sequence // //------------------------------------- */ void Ansi(void) { unsigned key; char c; char str[10]; int Pn[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i=0,p=0; static int oldx=1; static int oldy=1; /* While we are waiting for the next character, keep */ /* checking for keys. */ while((c = ComGetc(com)) == 0) if(bioskey(1)) { key = bioskey(0); ComPutc(com, (char)key); } /* If the next character is a '[' it is probably an ansi */ /* sequence. If the next character is not a '[', print the */ /* previous escape character followed by the new character. */ if(c != '[') { cprintf("\x1b%c",c); return; } while(1) { /* Read the rest of the ansi sequence, while also checking */ /* for keys. */ while((c = ComGetc(com)) == 0) if(bioskey(1)) { key = bioskey(0); ComPutc(com, (char)key); } /* If the character is numeric, store it still it it's */ /* ascii form */ if(isdigit(c)) { *(str+i++) = c; continue; } /* When no more numeric characters are received, terminate */ /* the string and convert it to an integer, storing it in */ /* the parameter queue. */ *(str+i) = '\0'; i=0; Pn[p++] = atoi(str); /* Check for the ';' delimiter */ if(c == ';') continue; /* When no more numeric parameters are received, the */ /* command should be next. Now we can actually process */ /* the command using the stores parameters */ else { switch(c) { /* (CUP) set cursor position */ case 'H': case 'F': y = Pn[0] ? Pn[0] : 1; x = Pn[1] ? Pn[1] : 1; return; /* (CUU) cursor up */ case 'A': y -= Pn[0]; if(y < 1) y = 1; return; /* (CUD) cursor down */ case 'B': y += Pn[0]; if(y > 24) y = 24; return; /* (CUF) cursor forward */ case 'C': x += Pn[0]; if(x >80) x = 80; return; /* (CUB) cursor backward */ case 'D': x -= Pn[0]; if(x < 1) x = 1; return; /* (SCP) save cursor position */ case 's': oldx = x; oldy = y; return; /* (RCP) restore cursor position */ case 'u': x = oldx; y = oldy; return; /* clear screen */ case 'J': if(Pn[0] == 2) { clrscr(); x=1; y=1; } else { window(1,wherey(),80,24); clrscr(); window(1,1,80,24); gotoxy(x, y); } return; /* (EL) erase line */ case 'K': clreol(); return; /* An attribute command is more elaborate than the */ /* others because it may have many numeric parameters */ case 'm': for(i=0; i= 30 && Pn[i] <= 37) { attrib &= 0xf8; attrib |= (Pn[i] - 30); } /* values from 40 to 47 define the background color */ if(Pn[i] >= 40 && Pn[i] <= 47) { attrib &= 0x8f; attrib |= ((Pn[i] - 40) << 4); } /* values from 0 to 7 define the other attributes */ if(Pn[i] >= 0 && Pn[i] <= 7) switch(Pn[i]) { case 0: attrib = NORM; break; case 1: attrib |= BOLD; break; case 2: attrib &= FAINT; break; case 5: case 6: attrib |= BLINK; break; case 7: attrib ^= REVRS; break; default: attrib = NORM; } } /* The red and blue bits in the ansi standard are */ /* reversed relative to the IBM. Therefore, before we */ /* set the new attributes, if these bits are in */ /* opposite states toggled. This must be done for */ /* both the foreground and background. */ if((attrib & 0x05) == 0x04 || (attrib & 0x05) == 0x01) attrib ^= 0x05; if((attrib & 0x50) == 0x40 || (attrib & 0x50) == 0x10) attrib ^= 0x50; textattr(attrib); default: return; } } } } /* //------------------------------------- // // check for errors // //------------------------------------- */ void CheckError(void) { unsigned comerror; static unsigned last_error = 0; static int err_flg = 0; char *errmsg[]= { { "no errors " }, { "break detect " }, { "frame error " }, { "parity error " }, { "overrun " }, { "rx overflow " }, { "tx overflow " } }; comerror = ComError(com); /* Check if the error code has changed since the last call. */ /* if it has not, there is no need to update the status line. */ /* If the error code has changed, the error conditions are */ /* checked in the order of their priority */ if(comerror != last_error) { last_error = comerror; if(comerror & BREAK) err_flg = 1; else if(comerror & FRAMING) err_flg = 2; else if(comerror & PARITY) err_flg = 3; else if(comerror & OVERUN) err_flg = 4; else if(comerror & RXFULL) err_flg = 5; else if(comerror & TXFULL) err_flg = 6; else err_flg = 0; /* Update the status line with the new information */ window(1,25, 80, 25); textattr(BLUE|(LIGHTGRAY<<4)); gotoxy(ERR_X,1); cprintf(errmsg[err_flg]); /* restore the normal screen */ textattr(attrib); window(1,1,80,24); gotoxy(x, y); } } /* //------------------------------------- // // check modem status // //------------------------------------- */ void CheckStatus(void) { unsigned status; static unsigned last_status = 1; int i; status = ComStatus(com); /* Check if the modem status has changed since the last call. */ /* if it has not, there is no need to update the status line. */ /* If the modem status has changed, each bit is checked for */ /* it's condition and printed on the status line */ if(status != last_status) { last_status = status; window(1,25, 80, 25); textattr(BLUE|(LIGHTGRAY<<4)); /* On each iteration, i = the x coordinate in the status */ /* line. The bits are checked for left to right. The */ /* statement !!(status & 0x10) resolves to 1 if the bit */ /* is set or 0 if the bit is clear. By adding 0x30 the */ /* 1 or 0 is converted to an ascii character */ for(i=STAT_X; i>= 1; } /* restore the normal screen */ textattr(attrib); window(1,1,80,24); gotoxy(x, y); } } /* //------------------------------------- // // Upload a file // //------------------------------------- */ void Upload(void) { char filename[83]; int rv; cputs("\r\n"); x = wherex(); y = wherey(); *filename = 80; cputs("File name > "); cgets(filename); rv = XmodemTx(com, filename + 2, callback); clreol(); cputs(*(Xmsg + rv)); cputs("\r\n"); x = wherex(); y = wherey(); } /* //------------------------------------- // // Download a file // //------------------------------------- */ void Download(void) { char filename[83]; int rv; cputs("\r\n"); x = wherex(); y = wherey(); *filename = 80; cputs("File name for your computer > "); cgets(filename); rv = XmodemRx(com, filename + 2, callback); clreol(); cputs(*(Xmsg + rv)); cputs("\r\n"); x = wherex(); y = wherey(); } /* //------------------------------------- // // Xmodem callback function // //------------------------------------- */ int callback(int msg, XPARAM param) { static retry = 10; const char *_err_txt; static const char *err_txt[] = { "OVER RUN", "BAD BLOCK", "BAD BLOCK CHECK", "TIME OUT", "TRANSFER CANCELED" }; switch(msg) { case XM_IDLE: break; case XM_START: cprintf("\r\nWaiting..."); break; case XM_BLOCKCHECK: cprintf("\r\nblock check:%s\r\n", param == 0 ? "CHECKSUM" : "CRC"); break; case XM_BLOCK: if(retry < 10) { retry = 10; putchar('\n'); } cprintf("\rblock:%d bytes: %ld", param, ((long)param) << 7); break; case XM_EOT: cprintf("\r\nEOT"); break; case XM_DONE: break; case XM_ERROR: if(param & OVERUN) _err_txt = err_txt[0]; else _err_txt = err_txt[param >> 12]; cprintf("\r\nERROR:%s", _err_txt); if(retry-- == 0) return(1); } if(kbhit()) { if(getch() == 0x1b) return(1); } return(0); } /* //------------------------------------- // // Microsoft portability // //------------------------------------- */ #if M_I86 int wherex(void) { byte rv; _asm { mov bh,0 mov ah,3 int 10h inc dl mov rv,dl } return(rv); } int wherey(void) { byte rv; _asm { mov bh,0 mov ah,3 int 10h inc dh mov rv,dh } return(rv); } void delay (clock_t milliseconds) { milliseconds += clock(); while(clock() < milliseconds); } void clreol(void) { cprintf("%*c", 80 - wherex() - 1, ' '); } int cprintf(const char *fmt, ...) { va_list list; int rv; char buf[129]; va_start(list, fmt); rv = vsprintf(buf, fmt, list); va_end(list); _outtext(buf); return(rv); } #endif