[net.micro.pc] Communication program for IBM PC/XT

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

------