home *** CD-ROM | disk | FTP | other *** search
- Unit V24;
-
- { **************************************************************************
- Name: V24.PAS
- Version: 1.0
- Edit Date: 30.1.1992
- Author: Frank R. Seidinger
- Comment: V24 (RS232) communication with Turbo Pascal 6.0
- **************************************************************************
-
- This unit implements an interrupt driven V24 handler for Turbo Pascal 6.0.
- It is based on ideas from Mike Halliday and Christian Phillips. V24.PAS
- can be used freely. If you want to modify it, please don't forget to
- mention the authors.
-
- To start communications use the function OpenCom. If opening the requested
- com-port was successful, the function returns TRUE, otherwise FALSE. There
- are several parameters to pass to OpenCom to meet nearly all desires one
- can have when tinking off interrupt driven communication. Beside the
- normal parameters that a normal V24 packet has (baud rate, parity, number
- of data bits and stop bits) OpenCom is able to dynaically adjust the
- ringbuffer size for incoming characters via the MyBuffSize parameter.
- Possible values can be KB1, KB2, KB4, KB8 and KB16. The ringbuffer's
- memory is allocated on the system heap by calling the Turbo Pascal
- procedure GetMem. Also OpenCom tries to use the FIFO features of the
- xx16550xx chipset if requested by the ComMode parameter.
-
- To end communication use the procedure CloseCom. This procedure resets the
- aktual com port in use and deaktivates the interrupt vectors uses by the
- V24 unit. Calling OpenCom automatically calls CloseCom thus gives the
- possibility to switch few or any com port parameters by calling OpenCom
- again. Therefore it is no problem to switch to another com port for
- example.
-
- To meet the greatest flexibility in assigning different com ports, the
- OpenCom parameter ComPort takes default values for standard ports Com1
- through Com4. Additionally it it possible to set a none standard com port
- by selecting UserCom for the ComPort parameter and setting the desired
- com port parameters through the function SetUserCom. SetUserCom needs to
- know on witch address the com port is located and what IRQ line (Interrupt
- request) it uses. Possible values for IrqNr are are 2,3,4,5 and 7.
- SetUserCom should be called before using OpenCom.
-
- After successfully opening a com port you are able to send and receive
- characters throug the V24 hardware. Sending a character is done with
- PutComData and receiving with GetComData. If there are no characters
- waiting in the ringbuffer, GetComData returns zero (ASCII NUL). You are
- able to check wether there are waiting characters by using the boolean
- function IsComDataAvail.
-
- If you want to use the xx16550xx chipset features, there are several
- possibilities for the ComMode parameter when calling OpenCom:
-
- Normal - No xx16650xx features are used. This is the recommended mode
- when using the 8250xx chipset.
-
- FIFOComp - The xx16550xx is used in compatible mode. In this mode the
- FIFO is disabled and the chipset behaves like a normal
- 8250xx chipset.
-
- FIFO1 - The xx16550xx FIFO is enabled and the chipset will generate
- an interrupt each time a character is placed into the FIFO.
- This mode also results the same behaviour as the FIFOComp
- mode does.
-
- FIFO4 - The xx16550xx FIFO is enabled and the chipset will generate
- an interrupt if the forth character is placed into the FIFO.
- The interrupt handler reads all four characters at the same
- time. Thus the interrupt overhead is reduced by four and
- results in better overall performance.
-
- FIFO8 - Like FIFO4 but generates an interrupt if the eighth
- character is placed into the FIFO.
-
- FIFO14 - Like FIFO4 but generates an interrupt if the fourteenth
- character is placed into the FIFO.
-
-
- If you have questions, please contact:
-
- address:
- **************************************
- BrainLab
- Frank R. Seidinger & Andreas Schumm
- Koloniestraße 71
- W-1000 Berlin 65
-
- Federal Republik of Germany
- **************************************
-
- or via e-mail:
- **************************************
- 1st: docbrain@netmbx.in-berlin.de
- 2nd: seid4000@w250zrz.zrz.tu-berlin.de
- **************************************
-
- ************************************************************************** }
-
- Interface
-
- Const XON = $0001;
- CTS = $0002;
- DSR = $0004;
- StopFlag = $8000;
-
- Type ComPortType = (Com1,Com2,Com3,Com4,UserCom,NoCom);
- ComBaudType = (b110,b150,b300,b600,b1200,b2400,b4800,b9600,b19200,
- b38400,b57600,b115200);
- ComDataType = (Space,Odd,Mark,Even,NoParity);
- ComBitsType = (d5,d6,d7,d8);
- ComStopType = (s1,s2);
- ComBuffType = (Kb1,Kb2,Kb4,Kb8,Kb16);
-
- ComModeType = (Normal,FIFOComp,FIFO1,FIFO4,FIFO8,FIFO14);
-
- Function OpenCom(ComPort : ComPortType; Baudrate : ComBaudType;
- Data : ComBitsType; Parity : ComDataType;
- StopBits : ComStopType; ComMode : ComModeType;
- MyBuffSize : ComBuffType) : Boolean;
-
- Procedure CloseCom;
-
- Function IsComDataAvail : Boolean;
-
- Function GetComData : Byte;
-
- Procedure PutComData(Data : Byte);
-
- Function SetUserComPort(ComBaseAddr : Word; IrqNr : Byte) : Boolean;
-
- Procedure SetHandShake(MyHandShake : Word);
-
- Implementation
-
- Uses DOS;
-
- Type BufType = Array[0..$3FFF] of Byte; { type of ringbuffer}
- BufPtrType = ^BufType;
-
- Const ComAddrArr : Array[ComPortType] of Word =
- ($03F8,$02F8,$02E8,$03E8,$FFFF,$FFFF);
- ComIntMasks : Array[ComPortType] of Byte = ($EF,$F7,$EF,$F7,$FF,$FF);
- ComIntVecs : Array[ComPortType] of Byte = ($0C,$0B,$0C,$0C,$FF,$FF);
-
- BuffSizeArr : Array[ComBuffType] of Word =
- ($0400,$0800,$1000,$2000,$4000);
-
- Buffer : BufPtrType = NIL; { pointer to com port ringbuffer }
- BufferSize : Word = 1024; { aktual size of ringbuffer }
-
- Top : Word = 0;
- Bottom : Word = 0; { Top and Bottom of ringbuffer }
-
- ComPortAddr : Word = $FFFF; { address of com port in use }
-
- RBR = $00; { xF8 Receive Buffer Register }
- THR = $00; { xF8 Transmitter Holding Register }
- IER = $01; { xF9 Interrupt Enable Register }
- IIR = $02; { xFA Interrupt Identification Register }
- LCR = $03; { xFB Line Control Register }
- MCR = $04; { xFC Modem Control Register }
- LSR = $05; { xFD Line Status Register }
- MSR = $06; { xFE Modem Status Register }
-
- DLL = $00; { xF8 Divisor Latch Low Byte }
- DLH = $01; { xF9 Divisor Latch Hi Byte }
-
- CMD8259 = $20; { Interrupt Controller Command Register }
- IMR8259 = $21; { Interrupt Controller Mask Register }
-
- AktivePort : ComPortType = NoCom; { aktive port ID }
- FIFOMode : ComModeType = Normal; { 16550 FIFO ID }
-
- HandShake : Word = 0;
-
- Var OldComIntVec : Pointer;
-
- Procedure ClearPendingInterrupts;
- { Clears all interrupts, that are pending in the UART hardware }
- Var Dummy : Byte;
- Begin
- If (AktivePort <> NoCom) Then
- Begin
- While ((Port[ComPortAddr+IIR] And 1) = 0) do
- Begin { while interrupts are pending }
- Dummy := Port[ComPortAddr+LSR]; { reset priority level 1 }
- Dummy := Port[ComPortAddr+RBR]; { reset priority level 2 }
- Dummy := Port[ComPortAddr+MSR]; { reset priority level 4 }
- { priority level 3 is cleared by the while statement }
- Port[CMD8259] := $20; { reset interrupt controller }
- End;
- End;
- End;
-
- Function IsComDataAvail : Boolean;
- Begin
- IsComDataAvail := (Top <> Bottom);
- End;
-
- Function GetComData : Byte;
- Var Free : Integer;
- Begin
- If (AktivePort <> NoCom) Then
- Begin
- If (Top <> Bottom) Then
- Begin
- GetComData := Buffer^[Bottom];
- Bottom := Succ(Bottom) mod BufferSize;
- If (HandShake <> 0) and ((HandShake and StopFlag) <> 0) Then
- Begin
- If Top > Bottom Then
- Free := Bottom - Top + BufferSize
- Else
- Free := Bottom - (Top + BufferSize) + BufferSize;
- If (Free > (BufferSize-256)) Then
- Begin
- If ((HandShake and XON) <> 0) Then
- PutComData(17);
- If ((HandShake and CTS) <> 0) Then
- Port[ComPortAddr+MCR] := Port[ComPortAddr+MCR] or $2;
- If ((HandShake and DSR) <> 0) Then
- Port[ComPortAddr+MCR] := Port[ComPortAddr+MCR] or $1;
- HandShake := HandShake and Not StopFlag;
- End;
- End;
- End
- Else GetComData := 0;
- End
- Else GetComData := 0;
- End;
-
- Procedure PutComData(Data : Byte);
- Begin
- If (AktivePort <> NoCom) Then
- Begin
- Repeat
- { wait for THRE = 1 }
- Until ((Port[ComPortAddr+LSR] and $20) = $20);
- Port[ComPortAddr] := Data;
- End;
- End;
-
- Procedure NewComIntSupport; FAR;
- Var NextPos : Word;
- MyByte : Byte;
- Free : Integer;
- Begin
- While ((Port[ComPortAddr+LSR] and $1) <> 0) do
- Begin
- MyByte := Port[ComPortAddr];
- NextPos := Succ(Top) mod BufferSize;
- If (NextPos <> Bottom) Then
- Begin
- Buffer^[Top] := MyByte;
- Top := NextPos;
- End;
- End;
- Port[CMD8259] := $20;
- If (HandShake <> 0) and ((HandShake and StopFlag) = 0) Then
- Begin
- If (NextPos < Bottom) Then
- NextPos := NextPos + BufferSize;
- Free := Bottom - NextPos + BufferSize;
- If Free < 256 Then
- Begin
- If ((HandShake and XON) <> 0) Then
- PutComData(19);
- If ((HandShake and CTS) <> 0) Then
- Port[ComPortAddr+MCR] := Port[ComPortAddr+MCR] and $FD;
- If ((HandShake and DSR) <> 0) Then
- Port[ComPortAddr+MCR] := Port[ComPortAddr+MCR] and $FE;
- HandShake := HandShake or StopFlag;
- End;
- End;
- End;
-
- Procedure NewComInt; FAR; ASSEMBLER;
- ASM
- push ax
- push bx
- push cx
- push dx
- push si
- push di
- push ds
- push es
-
- mov ax,SEG @Data { load turbo pascal ds }
- mov ds,ax { and set ds }
-
- call NewComIntSupport;
-
- pop es
- pop ds
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
-
- iret { Bye, bye }
- End;
-
- Procedure CloseCom;
- Begin
- If (AktivePort <> NoCom) and
- (ComPortAddr <> $FFFF) and
- (ComIntMasks[AktivePort] <> $FF) Then
- Begin
- Inline($FA);
- Port[ComPortAddr+MCR] := 0;
- Port[IMR8259] := Port[IMR8259] or (Not ComIntMasks[AktivePort]);
- Port[ComPortAddr+IER] := 0;
- ClearPendingInterrupts;
- SetIntVec(ComIntVecs[AktivePort],OldComIntVec);
- ComPortAddr := $FFFF;
- AktivePort := NoCom;
- Inline($FB);
- End;
- End;
-
- Function OpenCom(ComPort : ComPortType; Baudrate : ComBaudType;
- Data : ComBitsType; Parity : ComDataType;
- StopBits : ComStopType; ComMode : ComModeType;
- MyBuffSize : ComBuffType) : Boolean;
- Const BaudConst : Array[ComBaudType] of Word =
- ($417,$300,$180,$C0,$60,$30,$18,$0C,$06,$03,$02,$01);
- ParityConst : Array[ComDataType] of Byte =
- ($38,$08,$28,$18,$00);
- BitsConst : Array[ComBitsType] of Byte =
- ($00,$01,$02,$03);
- StopConst : Array[ComStopType] of Byte =
- ($00,$04);
- ModeConst : Array[ComModeType] of Byte =
- ($06,$06,$07,$47,$87,$C7);
- Var NewBuffSize : Word;
- Begin
- OpenCom := False;
- If (ComPort <> NoCom) and
- (ComAddrArr[ComPort] <> $FFFF) and
- (ComIntMasks[ComPort] <> $FF) Then { all values are correct }
- Begin
- Port[ComAddrArr[ComPort]+IER] := $F0; { disable interrupts }
- If Port[ComAddrArr[ComPort]+IER] = 0 Then
- Begin { new port is valid }
- CloseCom; { close old port }
- If (Buffer <> NIL) Then
- FreeMem(Buffer,BufferSize); { free mem of old buffer }
- NewBuffSize := BuffSizeArr[MyBuffSize]; { calculate new buffer size }
- GetMem(Buffer,NewBuffSize); { allocate new buffer memory }
- BufferSize := NewBuffSize; { set new buffer size }
- Top := 0;
- Bottom := 0; { initialize ringbuffer }
- ComPortAddr := ComAddrArr[ComPort]; { set com port address }
- AktivePort := ComPort; { keep aktive port in mind }
- Inline($FA); { STI }
- Port[ComPortAddr+IER] := 0; { disable all interrupts }
- ClearPendingInterrupts; { clear all interrupts }
- GetIntVec(ComIntVecs[ComPort],OldComIntVec); { get old interrupt vector }
- SetIntVec(ComIntVecs[ComPort],@NewComInt); { install new handler }
- Port[ComPortAddr+IIR] := ModeConst[ComMode]; { set FIFO Mode }
- Port[ComPortAddr+LCR] := $80; { divisor latch access enable }
- Port[ComPortAddr+DLH] := Hi(BaudConst[Baudrate]);
- Port[ComPortAddr+DLL] := Lo(BaudConst[Baudrate]); { set baud rate }
- Port[ComPortAddr+LCR] := ($00 or ParityConst[Parity]
- or BitsConst[Data]
- or StopConst[StopBits]);
- Port[ComPortAddr+MCR] := $0B; { set RTS,DTR,OUT2 }
- Port[ComPortAddr+IER] := $01; { enable interrupt }
- Port[IMR8259] := Port[IMR8259] and ComIntMasks[ComPort];
- Inline($FB); { STI }
- OpenCom := True;
- End;
- End;
- End;
-
- Function SetUserComPort(ComBaseAddr : Word; IrqNr : Byte) : Boolean;
- Const IrqMasks : Array[0..7] of Byte = ($01,$02,$04,$08,$10,$20,$40,$80);
- Begin
- If IrqNr IN [2,3,4,5,7] Then
- Begin
- ComIntVecs[UserCom] := IrqNr + 8;
- ComIntMasks[UserCom] := Not IrqMasks[IrqNr];
- ComAddrArr[UserCom] := ComBaseAddr;
- SetUserComPort := True;
- End
- Else SetUserComPort := False;
- End;
-
- Procedure SetHandShake(MyHandShake : Word);
- Begin
- HandShake := HandShake and $8000;
- HandShake := HandShake or (MyHandShake and (XON or CTS or DSR));
- End;
-
- End.