ali@bradley.UUCP (07/09/84)
#N:bradley:900003:000:13218 bradley!ali Jul 8 17:53:00 1984 ----- Here is a communication program that somebody out there may want to use. It includes library routines to interface a high level language like C to to the hardware of an IBM PC or XT. I wrote it to communicate with a UNIX (Trademark ...) system, but there is nothing in the program to limit it to that operating system. The uploading is as simple as reading a file on the IBM and sending it as if you are typing it(note the keyboard is also active during the upload or download, so anything that you type will be sent too) I break the pair of CR,LF into just a CR so that upload looks like the original on the PC(without the double spacing, especially when an editor like ED is receiving the file). When I wrote the assembly support routines I had DeSmet C in mind, but you can modify it to run with other languages easily. Enjoy. Ali Ezzet, Bradley University. Peoria, IL. ----- #define ALTQ 16 #define ALTD 32 #define DEL 83 #define ALTU 22 #define ALTB 48 char dlfn[64],ulfn[64],*cbuf,*em="Bad flag"; int ch,cr=0,dlfd,ulfd,ulf=0,dlf=0; int port=0,baud=300,prty=0,stop=0,wlen=8; int cbufl=0x1000,i; main(argc,argv) int argc; char *argv[]; { argv++; while(argc > 1) { if(argv[0][0] == '-') { switch(argv[0][1]) { case 'p': if((port= argv[0][2]-'0') != 0 && port != 1) { em="p: must be 0 or 1."; goto usage; } break; case 'b': baud=0; i=2; while((ch= argv[0][i++]) >= '0' && ch <= '9') baud= (baud * 10) + (ch - '0'); if(ch) { em="b: non-digits"; goto usage; } break; case 'e': prty=3; break; case 'o': prty=1; break; case 'n': prty=0; break; case 'm': prty=5; break; case 's': if((stop = argv[0][2]-'0') > 1) { em="s: must be 0 or 1"; goto usage; } break; case 'w': if((wlen= argv[0][2]-'0') < 5 || wlen > 8) { em="w: must be 5 thru 8"; goto usage; } break; case 'l': cbufl=0; i=2; while((ch= argv[0][i++]) >= '0' && ch <= '9') cbufl= (cbufl * 10) + (ch - '0'); if(ch) { em="l: non-digits"; goto usage; } break; default : usage : prs("Usage: pgm [-p#] [-b####] [-{e|o|n|m}]"); prs(" [-s#] [-w#] [-l#####]\r\n"); prs(em); prs("\r\n"); exit(1); } } else goto usage; argv++; argc--; } freeall(8000); /* save 8000 bytes from stack and locals */ if((cbuf=malloc(cbufl)) == 0) { prs("Allocation error, no room for com. buffer\r\n"); exit(1); } if((i=com_init(port,baud,prty,stop,wlen,cbuf,cbufl)) < 0) { prs("Initialization failed.\r\n"); switch(i) { case -1: prs("port number ?\r\n"); break; case -2: prs("baud rate ?\r\n"); break; case -3: prs("already active.\r\n"); } exit(1); } for(;;) { if((ch=kbd_getc())) if(ch < 256) com_putc(ch); else func(ch); if(ulf) if((ch=getc(ulfd)) < 0) { close(ulfd); ulf=0; } else { if(!(cr && ch == '\n')) com_putc(ch); if(ch == '\r') cr=1; else cr=0; } if((ch=com_getc()) != -1) { dos_putc(ch); if(dlf) putc(ch&0x7f,dlfd); } } } func(ch) int ch; { switch((ch>>8)&0xff) { case DEL : com_putc(127); break; case ALTB: com_break(); break; case ALTD: if(dlf) { close(dlfd); prs("\r\nDownload stopped.\r\n"); dlf=0; return; } prs("\r\nDownload,\r\nEnter IBM file name : "); gts(dlfn); if((dlfd=creat(dlfn)) < 0) { prs("Cannot create: "); prs(dlfn); prs("\r\n"); prs("Download aborted.\r\n"); } else dlf=1; break; case ALTU: if(ulf) { close(ulfd); prs("\r\nUpload stopped.\r\n"); ulf=0; return; } prs("\r\nUpload,\r\nEnter IBM file name : "); gts(ulfn); if((ulfd=open(ulfn,0)) < 0) { prs("Cannot open : "); prs(ulfn); prs("\r\n"); prs("Upload aborted.\r\n"); } else ulf=1; break; case ALTQ: com_term(); exit(0); } } gts(s) char *s; { char c,*s1; s1=s; while((c=getchar()) != '\r') if(c == '\b') if( s > s1) { prs(" \b"); s--; } else continue; else *s++ = c; *s = '\0'; dos_putc('\n'); } prs(s) char *s; { char c; while((c= *s++)) dos_putc(c); } ;;;;;------- the assembley langauge support routines. ;------ comlib ; ; This library contains several low level support routines for ; communication programs. The routines were written to support ; a C program on an IBM personal computer running DOS 2.00 or ; higher. I use the DeSmet C compiler, You may need to modify ; the routines to accomodate other compilers. ; cseg ; baud rate table, the first entry is the value passed to the ; initialization routine from the main program, the second ; entry is the actual baud rate divisor value to be sent to the ; baud rate generator latches. ; baudt: dw 50, 2304 dw 75, 1536 dw 110, 1047 dw 134, 857 dw 150, 768 dw 300, 384 dw 600, 192 dw 1200, 96 dw 1800, 64 dw 2000, 58 dw 2400, 48 dw 3600, 32 dw 4800, 24 dw 7200, 16 dw 9600, 12 ba8250 dw 0 ;base address of the 8250 card. active db 0 ;routines already active flag. imr db 0 ;original interrupt mask register value. b_first dw 0 ;first word address of comm buffer. b_in dw 0 ;current free byte pointer. b_seg dw 0 ;segment address of buffer. b_out dw 0 ;current byte to read. b_limit dw 0 ;last word address of buffer. ;------ com_init ; ; initialize the 8250, 8259, and establish addressing for the ; communication buffer. ; ; com_init(port,baud,prty,stop,wlen,buff,buffl) ; ; port port number (0,1). ; baud baud rate (50..9600) check table above. ; prty parity setting, this is the tricky one, ; bit 0 parity enable. ; bit 1 even parity. ; bit 2 stick parity. ; all other bits are masked off. ; stop number of stop bits, ; 0 1 stop bit ; 1 2 stop bits when wlen = 6,7 or 8. ; 1-1/2 bits when wlen = 5. ; wlen word length (5,6,7 or 8) ; buff address of communication buffer. ; buffl communication buffer length. ; ; In case of an error, the routine will return a negative ; integer of: ; -1 port number in error. either the port number ; is out of range or no communication card in ; system. ; -2 baud rate not in range. ; -3 communication routines already active, this ; is returned if two consecutive calls are ; made to the com_init routine without an ; intervening call to com_term. ; public com_init_ com_init_: cli ;no interrupts while initializing. push bp mov bp,sp cmp cs: byte active,0 mov ax,-3 jnz com_initx ;routines already active. mov bx,[bp+4] ;port number. cmp bx,1 ;check port number. mov ax,-1 ja com_initx ;port number out of range. push ds mov cx,40h mov ds,cx shl bx,1 mov si,bx mov dx,[bx] ;get base address of card. pop ds or dx,dx jz com_initx mov cs:word ba8250,dx ;save for now. mov al,80h ;divisor latch access bit. add dx,3 ;line control register. out dx,al sub dx,3 mov ax,[bp+6] ;get baud rate setting. mov bx,offset baudt mov cx,15 com_init1: cmp cs:[bx],ax jz com_init2 add bx,4 loop com_init1 mov ax,-2 jmp com_initx com_init2: mov ax,cs:[bx+2] ;get divisor latch setting. out dx,al ;set baud rate generator latches. inc dx mov al,ah out dx,al mov bx,30h ;int vector address for port 0. shl si,1 ;si=0 or 2. for port 0 or 1. sub bx,si ;establish correct vector addressing. xor ax,ax push ds mov ds,ax mov ax,offset isr8250 ;interrupt service routine offset. mov [bx],ax ;point to new routine. mov [bx+2],cs pop ds in al,21h ;get current interrupt mask register. mov cs:imr,al ;save for com_term. shr si,1 ;si=4 for port 1, 0 for port 0. shr si,1 mov cx,si mov ah,0efh ;establish mask. ror ah,cl and al,ah ;combine with other bits. out 21h,al ;let the 8259 know about it. mov ax,[bp+14] ;get buffer address. mov cs:b_first,ax ;establish buffer addressing. mov cs:b_in,ax mov cs:b_out,ax mov cs:b_seg,ds add ax,[bp+16] ;get buffer length. dec ax ;adjust length to address. mov cs:b_limit,ax ;set lwa. mov ax,[bp+12] ;get word length. sub ax,5 and al,3 ;keep only significant bits. mov ah,[bp+10] ;get stop bit setting. and ah,1 shl ah,1 shl ah,1 or al,ah ;combine with word length. mov ah,[bp+8] ;get stop bits. and ah,7 ;mask off any garbage. mov cl,3 shl ah,cl or al,ah ;combine with prty and wlen. add dx,2 ;line control register. out dx,al mov al,1 ;only data available enabled. sub dx,2 ;interrupt enable register. out dx,al mov al,0fh ;DTR, RTS, OUT1, and OUT2. add dx,3 ;modem control register. out dx,al sub dx,4 ;RBR. in al,dx xor ax,ax ;clear out any garbage in RBR. mov cs:byte active,1 ;active status on. com_initx: pop bp sti ;interrupts back on. ret ;------ com_term ; ; disable communications interrupt, restore original value ; of the interrupt mask register for the 8259. ; ; com_term(); ; public com_term_ com_term_: cli ;no interrupts while reseting mask. mov al,cs:imr out 21h,al mov cs:byte active,0 ;no longer active. ret ;------ isr8250 ; ; the 8250 interrupt service routine. ; isr8250: push ax push dx push di push es mov dx,cs:ba8250 ;RBR address. in al,dx ;get the byte first. mov di,cs:b_in ;establish buffer addressing. mov es,cs:b_seg cld stosb ;save the byte. cmp di,cs:b_limit ;check circular buffer. jnz isr8250a mov di,cs:b_first isr8250a: mov cs:b_in,di ;adjust pointer. mov al,20h ;EOI to the 8259. out 20h,al pop es pop di pop dx pop ax iret ;------ com_putc ; ; send a byte through the RS232 port. ; ; com_putc(c) ; char c; ; public com_putc_ com_putc_: push bp mov bp,sp mov dx,cs:ba8250 ;base address of the card. add dx,5 ;line status register. com_putc1: in al,dx ;is THR empty. test al,20h jz com_putc1 sub dx,5 ;go ahead send the byte. mov al,[bp+4] out dx,al pop bp ret ;------ com_getc ; ; get a character from the communication buffer, returns with ; a negative 1 (-1) in case of no characters in buffer. ; ; if((c=com_getc()) == -1) continue; ; else putc(c); ; public com_getc_ com_getc_: push bp mov bp,sp mov ax,-1 ;assume no characters in buff. mov si,cs:b_out cmp si,cs:b_in jz com_getcx push ds mov ds,cs:b_seg ;establish addressing. cld lodsb ;get the character. pop ds cmp si,cs:b_limit jnz com_getc1 mov si,cs:b_first com_getc1: mov cs:b_out,si xor ah,ah ;make sure it is positive. com_getcx: pop bp ret ;------ com_break ; ; send a break. ; public com_break_ com_break_: mov dx,cs:ba8250 add dx,3 in al,dx or al,60h out dx,al mov cx,3fffh com_break1: loop com_break1 xor al,60h out dx,al ret ;------ kbd_getc ; ; get a character from the keyboard if one is available, else ; return with a (0). ; public kbd_getc_ kbd_getc_: mov ah,1 int 16h mov ax,0 ;assume nothing available. jz kbd_getc1 int 16h ;get the character. or al,al ;check for extended code. jz kbd_getc1 xor ah,ah ;clear upper byte. kbd_getc1: ret ;------ dos_putc ; ; use DOS function call to put a character on the display, this ; allows the routine to use any device drivers that may be ; installed. ; ; dos_putc(c) ; char c; ; public dos_putc_ dos_putc_: push bp mov bp,sp mov dl,[bp+4] and dl,7fh ;clear upper bit. mov ah,2 int 21h pop bp ret end ------