[comp.binaries.ibm.pc.d] Looking for serial port code/samples/help

almquist@brahms.udel.edu (Squish) (03/12/91)

Anyone out there have some example source code, samples, docs, help, etc. on
programming the serial ports on a PC?  Thanks!

- Mike Almquist (almquist@brahms.udel.edu)

davem@hpmwtd.HP.COM (Dave McQuate) (03/14/91)

Here's a re-post from 26 Feb 1991 comp.os.msdos.programmer--

From david@kessner.denver.co.us Mon Feb 25 23:04:43 1991
Relay-Version: version Notes 2.8.2 (with RFA)  87/11/24; site hpmwdjm.HP.COM
From: david@kessner.denver.co.us (David D. Kessner)
Date: Tue, 26 Feb 1991 07:04:43 GMT
Date-Received: Tue, 26 Feb 1991 21:31:18 GMT
Subject: Serial routines
Message-ID: <1991Feb26.070443.7470@kessner.denver.co.us>
Organization: Kessner, Inc
Path: hpmwdjm!hpnmdla!hpl-opus!hplabs!sdd.hp.com!zaphod.mps.ohio-state.edu!magnus.ircc.ohio-state.edu!csn!kessner!david
Newsgroups: comp.os.msdos.programmer
Sender: david@kessner.denver.co.us (David Kessner)
Keywords: C ASSEMBLER TURBO SERIAL MODEM
Lines: 656

Here are the serial drivers that you all requested!  I think that there is
probably a different news group that this REALLY belongs to, but the source is
small and comp.os.msdos.programmer is where the original post was made...

There are two files, async.h and async.asm, included at the
end of this message.  Just look for the '------' cut here markers.

ASYNC.H is containes all of the funtion prototypes needed to use the 
assembly code from a C program, and ASYNC.ASM is the code itself.  I have
used these with Turbo C, and Turbo ASM without any problems.  It should work
with Microsoft C and ASM, but a bit of re-writing will be needed to make the 
assmebler part work.

Here is a short tutorial of the Async functions:

AsyncInit( int Port)
AsyncStop( void)
These functions start and stop the interrupt handlers.  No characters can be
recieved if it isn't started, and the computer will crash if they are not 
stopped before the program is exited.  The paramater 'port' is the COM port
to use, there are #defines in the header file that defines the values to be
used.  It would be 'more bug proof' if AsyncStop was called via the atexit()
function (check your compiler manual).


AsyncClear( void)
Clears the internal buffers.


AsyncOut( int c)
Sends out a character.


AsyncIn( void)
Returns the next character in the buffer or a NULL upon an empty buffer.  Use
AsyncInStat() to check for the number of characters in the buffer.


AsyncHand( int handshake)
Sets the status of the handshaking lines.  The values are defined in the 
header file and can be or'd together.  A typical call would look like this:
	AsyncHand( DTR | RTS);


AsyncSet( int Baud, int Control)
This sets the baud rate, paraty, etc, of the serial line.  The paramater 'Baud'
is the baud rate (ie 1200, 2400, 9600, etc), and 'control' should be 3 for 
8 bits, 1 stop bit, and no parity.


AsyncInStat( void)
This frunction returns the number of characters in the input buffer.


AsyncOutStat( void)
Returns the number of characters in the output buffer.


AsyncStat( void)
Returns the status of the handshaking lines.  Use the & and | oporaters with 
the values defined in the header file to extract any useful informaton out of
this value.

The values used include the normal DCD, RI, and CTS, but also include D_DCD for
'Delta-DCD' or CHANGE IN DCD.  This is useful in detecting changes in the 
handshaking lines since the last time the status was read.


These routines do have their limitations, however.  They cannot deal with more
than one serial port, and cannot take advantage of the 16550 UART.  There is 
also no routines for input and output of strings-- there is so many ways of 
doing it that I left it up to whoever used these routines.  

Enjoy, and if you have any questions, dont hesitate to ask.

					- David K


David Kessner - david@kessner.denver.co.us            | do {
1135 Fairfax, Denver CO  80220  (303) 377-1801 (p.m.) |    . . .
This is my system so I can say any damn thing I want! |    } while( jones);

---------------------------- async.h ----------------------------------------
#ifndef	_ASYNC_H_
#define	_ASYNC_H_

void		AsyncInit( int Port),
		AsyncStop( void),
		AsyncClear( void),
		AsyncOut( int c),
		AsyncHand( int handshake),
		AsyncSet( int Baud, int Control);
int		AsyncIn( void),
		AsyncInStat( void),
		AsyncOutStat( void);
unsigned	AsyncStat( void);

#define	COM1		0
#define	COM2		1
#define	COM3		2
#define	COM4		3

/* Defines for AsyncHand() */
#define	DTR		0x01
#define	RTS		0x02
#define	USER		0x04
#define	LOOPBACK	0x10

/* Defines for AsyncStat() */
#define	D_CTS	0x0100
#define	D_DSR	0x0200
#define	D_RI		0x0400
#define	D_DCD	0x0800
#define	CTS		0x1000
#define	DSR		0x2000
#define	RI		0x4000
#define	DCD		0x8000
#define	PARITY	0x0004
#define	THREMPTY	0x0020
#define	BREAKDET	0x1000

#endif
------------------------------ asnyc.asm -----------------------------------
;  Async Module.

	IDEAL
	MODEL	compact

EOI			equ	020h				; 8259 end-of-interupt
Ctrl8259_0	equ	020h				; 8259 port
Ctrl8259_1	equ	021h				; 8259 port (Masks)
BufSize		equ	1024				; Buffer Size




	DATASEG

; Various things to be set upon AsyncInit()
VectorNum		db	?				; Vector Number
EnableIRQ		db	?				; Mask to enable 8259 IRQ
DisableIRQ	db	?				; Mask to disable 8259 IRQ
VectorSeg		dw	?				; Old Vector Segment
VectorOfs		dw	?				; Old Vector Offset

; Register Addresses for the 8250 UART
Port			dw	?				; Port Base Address
LABEL RegStart word
THR			dw	?				; Transmitter Holding Register
RDR			dw	?				; Reciever Data Register
BRDL			dw	?				; Baud Rate Divisor, Low byte
BRDH			dw	?				; Baud Rate Divisor, High Byte
IER			dw	?				; Interupt Enable Register
IIR			dw	?				; Interupt Identification Register
LCR			dw	?				; Line Control Register
MCR			dw	?				; Modem Control Register
LSR			dw	?				; Line Status Register
MSR			dw	?				; Modem Status Register

; Buffer Data
RecBuffer		db	BufSize DUP (?)	; Recieve Buffer
RecHead		dw	?				; Buffer Head Pointer
RecTail		dw	?				; Buffer Tail Pointer
TransBuffer	db	BufSize DUP (?)	; Transmit Buffer
TransHead		dw	?				; Buffer Head Pointer
TransTail		dw	?				; Buffer Tail Pointer

; Register Offsets for the UART
RegOffsets	dw	0, 0, 0, 1, 1, 2, 3, 4, 5, 6


	CODESEG

	PUBLIC	_AsyncInit,	_AsyncClear, 	_AsyncStop
	PUBLIC	_AsyncIn,		_AsyncOut, 	_AsyncSet
	PUBLIC	_AsyncHand,	_AsyncStat,	_AsyncInStat
	PUBLIC	_AsyncOutStat


;-----------------------------------------------------------------------------
; AsyncClear						Empty the recieve buffer
;-----------------------------------------------------------------------------
; void	AsyncClear( void);
;
;
;-----------------------------------------------------------------------------
PROC	_AsyncClear
	cli
	push	ax
	mov	ax, offset RecBuffer
	mov	[RecHead], ax
	mov	[RecTail], ax
	mov	ax, offset TransBuffer
	mov	[TransHead], ax
	mov	[TransTail], ax
	pop	ax
	sti
	ret
ENDP	_AsyncClear



;-----------------------------------------------------------------------------
; AsyncInit					Initalize Serial Port and install ISR
;-----------------------------------------------------------------------------
;	void	AsyncInit( int port)
;
;	Where Port is
;		0  =  COM1
;		1  =  COM2
;		2  =  COM3
;		3  =  COM4
;
;-----------------------------------------------------------------------------
PROC	_AsyncInit
	ARG	CommPort:word

	push	bp
	mov	bp, sp

;---- Set various things according to com port number
	mov	ax, [CommPort]

;----- COM1
	cmp	ax, 0
	jne	@@1
	mov	[Port], 03F8h
	mov	[VectorNum], 0Ch
	mov	[EnableIRQ], 0EFh
	mov	[DisableIRQ], 10h
	jmp	@@Done
@@1:
;----- COM2
	cmp	ax, 1
	jne	@@2
	mov	[Port], 02F8h
	mov	[VectorNum], 0Bh
	mov	[EnableIRQ], 0F7h
	mov	[DisableIRQ], 08h
	jmp	@@Done
@@2:
;----- COM3
	cmp	ax, 2				; 2
	jne	@@3
	mov	[Port], 03E8h			; 03E8
	mov	[VectorNum], 0Ch		; 0C
	mov	[EnableIRQ], 0EFh		; EF
	mov	[DisableIRQ], 10h		; 10
	jmp	@@Done
@@3:
;----- COM4
	mov	[Port], 02E8h			; 02E8
	mov	[VectorNum], 0Bh		; 0B
	mov	[EnableIRQ], 0F7h		; F7
	mov	[DisableIRQ], 08h		; 08
@@Done:

;---- Compute Register locations
	mov	cx, 10
	mov	bx, offset RegOffsets
	push	di
	mov	di, offset RegStart
@@4:
	mov	ax, [bx]
	add	ax, [Port]
	mov	[di], ax
	add	bx, 2
	add	di, 2
	loop	@@4

	pop	di


;----- Initalize Buffer
	call	_AsyncClear

;--- Save and reassign interrupt vector
	push	ds						; Save Old Vector
	mov	al,[VectorNum]
	mov	ah,35h
	int	21h
	mov	[VectorSeg], es
	mov	[VectorOfs], bx

	mov	al, [VectorNum]
	push	cs						; Set New Vector
	pop	ds
	mov	dx, offset AsyncISR
	mov	ah, 25h
	int	21h
	pop	ds

;----- Enable 8259 interrupt (IRQ) line for this async adapter
	in	al, Ctrl8259_1
	and	al, [EnableIRQ]
	out	Ctrl8259_1, al

;----- Enable 8250 Interrupt-on-data-ready
	mov	dx, [LCR]				; Read Line control register and clear
	in	al, dx				; bit 7, the Divisor Latch Address
	and	al, 07Fh
	out	dx, al
	mov	dx, [IER]
	mov	al, 3
	out	dx, al

;----- Clear 8250 Status and data registers
@@10:
	mov	dx, [RDR]		; Clear RDR by reading port
	in	al, dx
	mov	dx, [LSR]		; Clear LSR
	in	al, dx
	mov	dx, [MSR]		; Clear MSR
	in	al, dx
	mov	dx, [IIR]		; Clear IIR
	in	al, dx
	test	al, 1
	jz	@@10

;----- Set Bit 3 of MCR -- Enable interupts
	mov	dx, [MCR]
	in	al, dx
	or	al, 08h
	out	dx, al

;----- Clear Buffer Just in case
	call	_AsyncClear

;----- Return
	pop	bp
	ret
ENDP	_AsyncInit



;-----------------------------------------------------------------------------
;  AsyncStop						Uninstall ISR
;-----------------------------------------------------------------------------
;	void  AsyncStop( void)
;-----------------------------------------------------------------------------
PROC	_AsyncStop
	push	bp
	mov	bp, sp

;----- Mask (disable) 8259 IRQ Interrupt
	in	al, Ctrl8259_1
	or	al, [DisableIRQ]
	out	Ctrl8259_1, al

;----- Disable 8250 interrupt
	mov	dx, [LCR]
	in	al, dx
	and	al, 07Fh
	out	dx, al
	mov	dx, [IER]
	xor	al, al
	out	dx, al

;----- Set bit 3 in MCR to 0
	mov	dx, [MCR]
	in	al, dx
	and	al, 0F7h
	out	dx, al

;----- Interrupts are disables.  Restore saved interrupt vector.
	push	ds
	mov	al, [VectorNum]
	mov	ah, 25h
	mov	dx, [VectorOfs]
	mov	ds, [VectorSeg]
	int	21h
	pop	ds

;----- Return
	pop	bp
	ret
ENDP	_AsyncStop



;-----------------------------------------------------------------------------
; AsyncISR					Async Interrupt Service Routine
;-----------------------------------------------------------------------------
;	To be called only as an interrupt.
;-----------------------------------------------------------------------------
PROC	AsyncISR
	push	ax					; Save Registers
	push	bx
	push	ds
	push	dx

	mov	ax, @data				; Address local data with ds
	mov	ds, ax

	mov	dx, [IIR]				; Check if data actually recieved
	in	al, dx
	and	al, 06h
	cmp	al, 04h
	je	@@recieve
	cmp	al, 02h
	jne	@@end

;----- Transmit A byte
@@transmit:
	mov	bx, [TransTail]
	cmp	bx, [TransHead]
	jne	@@1

	mov	dx, [IER]				; Buffer empty
	mov	al, 1
	out	dx, al				; Disable THR empty interrupt
	jmp	@@end

@@1:
	mov	al, [byte ptr bx]		; Get Byte
	inc	[TransTail]			; Update buffer pointer
	cmp	[word ptr TransTail], offset TransBuffer + BufSize
	jb	@@2
	mov	[TransTail], offset TransBuffer
@@2:
	mov	dx, [THR]
	out	dx, al
	jmp	@@end

;----- Recieve a byte
@@recieve:
	mov	dx, [RDR]				; Get Byte
	in	al, dx
	mov	bx, [RecHead]			; Store Byte in buffer
	mov	[byte ptr bx], al
	inc	bx					; Update RecHead
	cmp	bx, offset RecBuffer + BufSize
	jb	@@10
	mov	bx, offset RecBuffer
@@10:
	cmp	bx, [RecTail]
	jne	@@20
	mov	bx, [RecHead]			; Cancel Pointer advance on overflow
@@20:
	mov	[RecHead], bx			; Store new pointer

@@end:
	mov	al, EOI				; Signal end ot interrupt
	out	Ctrl8259_0, al

	pop	dx
	pop	ds
	pop	bx
	pop	ax
	iret
ENDP	AsyncISR



;-----------------------------------------------------------------------------
;	AsyncIn					Gets a byte from the input buffer
;-----------------------------------------------------------------------------
;	int	AsyncIn( void)
;-----------------------------------------------------------------------------
PROC	_AsyncIn
	push	bp
	mov	bp, sp

	xor	ax, ax				; Pre-Set result to 0
	mov	bx, [RecTail]
	cmp	bx, [RecHead]
	je	@@return
	mov	al, [byte ptr bx]
	inc	[RecTail]
	cmp	[word ptr RecTail], offset RecBuffer + BufSize
	jb	@@return
	mov	[RecTail], offset RecBuffer

@@return:
	pop	bp
	ret
ENDP	_AsyncIn



;-----------------------------------------------------------------------------
;	AsyncOut				Output a byte
;-----------------------------------------------------------------------------
;	void	AsyncOut( int c)
;-----------------------------------------------------------------------------
PROC	_AsyncOut
	ARG	CharOut:word

	push	bp
	mov	bp, sp

	mov	ax, [CharOut]

	mov	bx, [TransHead]
	mov	cx, bx
	inc	cx						; Compute NEW buffer position
	cmp	cx, offset TransBuffer + BufSize
	jb	@@1
	mov	cx, offset TransBuffer
@@1:
	cmp	cx, [TransTail]			; Wait for space in buffer
	je	@@1

	mov	[byte ptr bx], al			; Add byte to buffer
	mov	[TransHead], cx			; Update pointer

	mov	dx, [IER]					; Enable THR empty interrupt
	mov	al, 3
	out	dx, al

	pop	bp
	ret
ENDP	_AsyncOut



;-----------------------------------------------------------------------------
;	AsyncSet					Set communication paramaters
;-----------------------------------------------------------------------------
;	void	AsyncSet( int Baud, int Control)
;
;	Baud = 150, 300, 600, 1200, 2400, 4800, 9600, 19200, 28800, 38400, 57600
;	Control = The valure to place in the LCR
;-----------------------------------------------------------------------------
PROC	_AsyncSet
	ARG	Baud:word, Control:word

	push	bp
	mov	bp, sp

	mov	bx, [Baud]
	cmp	bx, 0
	je	@@abort

	mov	ax, 0C200h		; Baud rate divisor = 115200 / Baud
	mov	dx, 0001h			; 115200 = 0001C200h
	div	bx
	mov	cx, ax

	cli
	mov	dx, [LCR]			; Set Port Toggle to BRDL/BRDH registers
	mov	al, 0ffh
	out	dx, al

	mov	dx, [BRDL]		; Set Baud Rate
	mov	al, cl
	out	dx, al
	mov	dx, [BRDH]
	mov	al, ch
	out	dx, al

	mov	dx, [LCR]			; Set LCR and Port Toggle
	mov	ax, [Control]
	and	al, 07Fh
	out	dx, al

	sti
@@abort:
	pop	bp
	ret
ENDP	_AsyncSet



;-----------------------------------------------------------------------------
;	AsyncInStat				Returns the # of characters in buffer
;-----------------------------------------------------------------------------
;	int	AsyncInStat( void)
;-----------------------------------------------------------------------------
PROC	_AsyncInStat
	push	bp
	mov	bp, sp

	mov	ax,[RecHead]
	sub	ax, [RecTail]
	jge	@@10
	add	ax, BufSize
@@10:

	pop	bp
	ret
ENDP	_AsyncInStat



;-----------------------------------------------------------------------------
;	AsyncOutStat				Returns the # of characters in buffer
;-----------------------------------------------------------------------------
;	int	AsyncOutStat( void)
;-----------------------------------------------------------------------------
PROC	_AsyncOutStat
	push	bp
	mov	bp, sp

	mov	ax,[TransHead]
	sub	ax, [TransTail]
	jge	@@10
	add	ax, BufSize
@@10:

	pop	bp
	ret
ENDP	_AsyncOutStat



;-----------------------------------------------------------------------------
;	AsyncHand					Sets various handshaking lines
;-----------------------------------------------------------------------------
;	void	AsyncHand( int Hand)
;-----------------------------------------------------------------------------
PROC	_AsyncHand
	ARG	Hand:word
	push	bp
	mov	bp, sp

	mov	dx, [MCR]
	mov	ax, [Hand]
	or	al, 08h				; Keep interrupt enable ON
	out	dx, al

	pop	bp
	ret
ENDP	_AsyncHand



;-----------------------------------------------------------------------------
;	AsyncStat					Returns Async/Modem status
;-----------------------------------------------------------------------------
;	unsigned	AsyncStat( void)
;
;	MSR is returned in the high byte, LSR in the low byte
;-----------------------------------------------------------------------------
PROC	_AsyncStat
	push	bp
	mov	bp, sp

	mov	dx, [MSR]
	in	al, dx
	mov	cl, al
	mov	dx, [LSR]
	in	al, dx			; LSR in low byte
	mov	ah, cl			; MSR in high byte

	pop	bp
	ret
ENDP	_AsyncStat


	END
-------------------------------The End--------------------------------------
-- 
David Kessner - david@kessner.denver.co.us            | do {
1135 Fairfax, Denver CO  80220  (303) 377-1801 (p.m.) |    . . .
This is my system so I can say any damn thing I want! |    } while( jones);