home *** CD-ROM | disk | FTP | other *** search
- { include file FULCOM.IBM
- IBM COMMUNICATIONS PROCEDURES}
-
- { Gary Hartman 73537,1362}
-
- {
- These procedures are part of a set of two comm port include files that
- are identical to the programmer, but handle the different hardware that
- is found in the TI Professional computer and the IBM PC computer. This
- enables you to write a applications programs that can be compiled for
- either machine with a simple include file substitution.
-
- The programmer interface includes full buffering for both Input and OUTPUT.
- Output buffering allows an applications program to output a complex screen
- or control data quickly, allowing the host computer to remain free for
- use even if the selected baud rate is relative slow with respect to the
- amount of data output. These procedures were originally developed for
- use in a control system program that operated a remote terminal and remote
- hardware interface driven from one computer.
-
- Much of the IBM interrupt procedures have been developed from the file
- INTERR.INC found in CompuServe Turbo Pascal SIG. The TI Professional procedures
- are based on the articles by Matt Lawrence in June 1986 issue of the
- magazine TI Professional Computing (Publications & Communications Inc.;
- 12416 Hymeadow Drive, Suite Two; Austin TX 78750-1896). The basic data
- structure was based on Matt Lawrence's ideas.
-
- The file FULCOM.PAS is a basic dumb-terminal program that demonstrates
- the use of these procedures.
-
- *****************************************************************************
-
-
- READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS
-
-
- The variables that are at a global level in the include file must remain
- there. Due to the nature of the 8088 interrupts and Turbo Pascal these
- procedures must not be included inside any another procedures or functions.
-
- You must declare outside (or include it inside this file) the following:
-
- type WorkLineType=string[255];
-
- var RegisterSet case integer of
- 1: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:integer);
- 2: (AL,AH,BL,BH,CL,CH,DL,DH:byte);
- end;
- DSStorage:integer absolute CSeg:$00A0;
-
- The variable RegisterSet is used for DOS calls (handy for almost any program);
- the type WorkLineType is use for string output; the variable DSStorage is used
- to store the contents of the DS register for use by the interrupt procedures
- to restore the DS register. You must at the begining of the program make
- the assignment:
-
- DSStorage:=dseg;
-
- With out this the any interrupt handler will not be able to access any
- global variables.
-
- ******************************************************************************
-
-
- USER ENTRY POINTS
-
- The buffers are known as InputBuffer[Com] and OutputBuffer[Com], where
- Com:byte is the comm port number (1 or 2). The overflow flag is addressed
- as InputBuffer[Com].Over or OutputBuffer[Com].Over.
-
- procedure SENDSTRING(Com:byte;S:WorkLineType);
-
- This inserts a string of characters into the selected OutputBuffer
- and initiates transmission in the transmitter is quiet.
-
-
- function BUFFEREMPTY(B:BufferType):boolean;
-
- Returns true if the buffer is empty. The logical value of:
- not(BUFFEREMPTY(InputBuffer[Com]))
- can be compared to the keypressed function of Turbo Pascal.
-
-
- function READBUFFER(B:BufferType):byte;
-
- Returns the rear of the selected buffer (usually an input buffer).
- You must check the buffer with not(BUFFEREMPTY(InputBuffer[Com]))
- before invoking this function. As written, only the lower 7
- bits are kept on incoming data. If you need all 8 simply remove
- the "anding" of the input data with $7F in the interrupt procedures
- (COMINTERRUPT1 or COMINTERRUPT2).
-
-
- procedure INITCOM(
- ComNum:byte;Baud:integer;Parity:char;WordSize:byte;StopBits:byte);
-
- Initiates the selected comm port to the values passed,sets the
- interrupt vector to point to the user installed routine, and
- asserts the DTR line.
-
-
- procedure TERMINATECOM(Com:byte);
-
- Un-asserts the DTR line and re-installs the original interrupt
- vectors for the selected comm port.
-
-
- function CDSTATUS:boolean;
-
- True if the DCD (Data Carrier Detect-pin 8) is asserted; otherwise
- false.
-
-
- function CTSSTATUS:boolean;
-
- True if the CTS (Clear To Send-pin 5) is asserted; otherwise
- false.
-
- procedure TXCONTROL(Com:byte;Assert:boolean);
-
- Sets the DTR line accroding to Assert.
-
- ******************************************************************************
-
- }
-
- const BufferSize=4095;
-
- type { Type declarations }
- Busy = Array [1..2] of boolean;
- BufferType=record
- Head,Tail:integer; { front, rear pointers }
- Over:boolean; { queue error, full ect }
- Data:array[0..BufferSize] of byte;
- end;
- ComBaseType=array[1..2] of integer;
-
-
- CONST
- irq4 = $30; { Interrupt vector address for }
- { COM1. }
- irq3 = $2C; { Vector for COM2. }
- ComBase:ComBaseType=($03F8,$02F8); { port addresses }
- { Offset to add to com#base for}
- intenreg = 1; { Interrupt enable register }
- intidreg = 2; { Interrupt id register }
- linectrl = 3; { Line control register }
- modemctrl = 4; { Modem control register }
- linestat = 5; { Line status register }
- modemstat = 6; { Modem status register }
- eoi = $20; { End of interrupt command }
-
- var
- ComVecSeg, { Segment of DOS set }
- ComVecOff:array[1..2] of integer; { Offset of DOS set com int. }
- Tbyte,Lbyte:integer; { global var for interrupt }
- ComBusy:array[1..2] of boolean; { Comport trans busy flags }
- InputBuffer:array[1..2] of BufferType; { input buffer }
- OutputBuffer:array[1..2] of BufferType; { output buffer }
-
- procedure COMINTERRUPT1;
-
- begin
- inline($50/$53/$51/$52/$57/$56/$06/$1E/$2E/$A1 /$A0 /$00/$50/$1F);
- Lbyte:=port[ComBase[1]+intidreg]; { Get Interrupt ID }
- Lbyte:=(lbyte shr 1) and $03; { Isolate ID bits }
- If Lbyte=1 then begin { Check for Transmit done }
- if OutputBuffer[1].Tail=OutputBuffer[1].Head then ComBusy[1]:=false
- else begin { output another character }
- OutputBuffer[1].Tail:=(OutputBuffer[1].Tail+1)
- mod sizeof(OutputBuffer[1].Data);
- port[ComBase[1]]:=OutputBuffer[1].Data[OutputBuffer[1].Tail];
- end;
- end
- else if Lbyte=2 then begin { Check for Received Data }
- Tbyte:=port[ComBase[1]]; { Get the character in the port}
- Lbyte:=port[ComBase[1]+linestat]; { Get the status of the port }
- if (InputBuffer[1].Head+1) mod sizeof(InputBuffer[1].Data)=
- InputBuffer[1].Tail
- then InputBuffer[1].Over:=true { buffer overflow }
- else begin { put into buffer }
- InputBuffer[1].Head:=(InputBuffer[1].Head+1)
- mod sizeof(InputBuffer[1].Data);
- InputBuffer[1].Data[InputBuffer[1].Head]:=Tbyte and $7F;
- { use only first 7 bits }
- end;
- end; { lbyte = 2 }
- port[$20]:=$20; { signal end of interrupt code }
- inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$5D/$89 /$EC/$5D/$CF );
- end; {COMINTERRUTP1}
-
- procedure COMINTERRUPT2;
-
- begin
- inline($50/$53/$51/$52/$57/$56/$06/$1E/$2E/$A1 /$A0 /$00/$50/$1F);
- Lbyte:=port[ComBase[2]+intidreg]; { Get Interrupt ID }
- Lbyte:=(lbyte shr 1) and $03; { Isolate ID bits }
- If Lbyte=1 then begin { Check for Transmit done }
- if OutputBuffer[2].Tail=OutputBuffer[2].Head then ComBusy[2]:=false
- else begin { output another character }
- OutputBuffer[2].Tail:=(OutputBuffer[2].Tail+1)
- mod sizeof(OutputBuffer[2].Data);
- port[ComBase[2]]:=OutputBuffer[2].Data[OutputBuffer[2].Tail];
- end;
- end
- else if Lbyte=2 then begin { Check for Received Data }
- Tbyte:=port[ComBase[2]]; { Get the character in the port}
- Lbyte:=port[ComBase[2]+linestat]; { Get the status of the port }
- if (InputBuffer[2].Head+1) mod sizeof(InputBuffer[2].Data)=
- InputBuffer[2].Tail
- then InputBuffer[2].Over:=true { buffer overflow }
- else begin { put into buffer }
- InputBuffer[2].Head:=(InputBuffer[2].Head+1)
- mod sizeof(InputBuffer[2].Data);
- InputBuffer[2].Data[InputBuffer[2].Head]:=Tbyte and $7F;
- { use only first 7 bits }
- end;
- end; { lbyte = 2 }
- port[$20]:=$20; { signal end of interrupt code }
- inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$5D/$89 /$EC/$5D/$CF );
- end; {COMINTERRUTP2}
-
- procedure TXCONTROL(Com:byte;Assert:boolean);
-
- {Sets the DTR line to Assert}
-
- var TByte:byte;
-
- begin {DTRSET}
- inline($FA); { disable interrupts }
- TByte:=Port[ComBase[Com]+ModemCtrl]; { get current state }
- if Assert then TByte:=TByte or $01 { assert DTR }
- else TByte:=TByte and $FE; { un-assert DTR }
- Port[ComBase[Com]+ModemCtrl]:=TByte; { rewrite ModemCtrl Reg }
- inline($FB); { turn interrupts back on}
- end; {TXCONTROL}
-
- procedure INTON(Com:byte);
-
- const
- DtrTrue=1;
- RtsTrue=2;
- Bit3True=8;
-
- var
- Tbyte : byte; { Temperary byte buffer }
-
- begin {INTON}
- Tbyte:=port[ComBase[Com]]; { Read the ports to clear any }
- Tbyte:=port[ComBase[Com]+linestat ]; { error conditions }
- port[ComBase[Com]+modemctrl]:=DtrTrue+RtsTrue+Bit3True;
- port[ComBase[Com]+intenreg]:=3; { Enable com port interrupts }
- TByte:=port[$21];
- with RegisterSet do begin
- AX:=$2500; { Load the function number for }
- { redefining an interrupt }
- DS:=cseg; { Get and set the segment and }
- case com of
- 1:dx:=ofs(COMINTERRUPT1); { offset of the handler }
- 2:dx:=ofs(COMINTERRUPT2); { offset of the handler }
- end;
- end;
- case com of
- 1:begin
- ComVecOff[1]:=memw[0000:irq4]; { Save the segment and offset }
- ComVecSeg[1]:=memw[0000:irq4+2]; {of the DOS interrupt handler }
- RegisterSet.AX:=RegisterSet.AX+$0C; { Use the COM1: interrupt }
- intr($21,RegisterSet); { Call DOS to reset INT 0C }
- port[$21]:=TByte and $EF;
- end;
- 2:begin
- ComVecOff[2]:=memw[0000:irq3]; { Same as above }
- ComVecSeg[2]:=memw[0000:irq3+2];
- RegisterSet.AX:=RegisterSet.AX+$0B;{ Use the COM2: interrupt }
- intr($21,RegisterSet); { Call DOS }
- port[$21]:=TByte and $F7;
- end;
- end;
- ComBusy[com]:=false; { Com port not busy }
- inline($FB); { Enable interrupts }
- end; {INTON}
-
- procedure CLEAR(var Buf:BufferType);
-
- { this procedure clears a Buffer }
-
- begin {CLEAR}
- Buf.Tail:=1;
- Buf.Head:=1;
- Buf.Over:=false;
- end; {CLEAR}
-
- procedure INITCOM(Com:byte;R:integer;P:char;B:byte;S:byte);
-
- const
- Bits7=2;
- Bits8=3;
- Stopbit1=0; { These are constants used }
- Stopbit2=4; { to define parity, stop bits, }
- Noparity=0; { data bits, etc. }
- Oddparity=8;
- Evenparity=24;
- Rate300=0;
- Rate1200=64;
- Rate2400=160;
- Rate4800=192;
- Rate9600=224;
-
- var
- Tlcr, { Line control register }
- TDLmsb, { Divisor latch MSB }
- TDLlsb : byte; { Divisor latch LSB }
- Bits : integer; { No of bits per char }
- StopBits : integer; { No of stop bits per char }
- SetParity : integer; { parity mode even, odd , none }
-
- begin {INITCOM}
- TDLmsb:=0; { Set DL MSB to 0 for 1200, }
- { 2400, 4800 and 9600 baud }
- case R of { Use case to check baud rate }
- 300:begin { Check for 300 baud }
- TDLmsb:=1; { Set DL MSB to 01 }
- TDLlsb:=$80; { Set DL LSB to 80 }
- end; { for a total of 0180 }
- 1200:TDLlsb:=$60; { 1200 set LSB to 60 }
- 2400:TDLlsb:=$30; { 2400 set LSB to $30 }
- 4800:TDLlsb:=$18; { 4800 set LSB to 18 }
- 9600:TDLlsb:=$0c; { 0C for 9600 baud }
- end;
- case P of { use case to check parity }
- 'E' : setparity:=evenparity; { set for even parity }
- 'O' : setparity:=oddparity; { set for odd parity }
- 'N' : setparity:=noparity; { set for no parity }
- else SetParity:=NoParity; { default is no parity }
- end;
- case S of { use case for stopbits }
- 1:StopBits:=Stopbit1; { one stopbit }
- 2:stopbits:=Stopbit2; { two stopbits }
- else Stopbits:=Stopbit1; { default to 1 stopbit }
- end;
- case B of { use case for bits per char }
- 8:Bits:=bits8; { set to eight bits }
- 7:Bits:=bits7; { set to seven bits }
- else bits:=bits8; { default to eight bits }
- end;
- inline($FA); { disable interupts }
- Tlcr:=port[ComBase[Com]]; { read the port to clear any }
- Tlcr:=port[ComBase[Com]+LineStat]; { error condition }
- port[ComBase[Com]+LineCtrl]:=Bits+StopBits+SetParity; { Set parameters }
- Tlcr:=port[ComBase[Com]+Linectrl]; { Get the Line control register}
- port[ComBase[Com]+linectrl]:=tlcr or $80; { Set Div Latch Access Bit }
- port[ComBase[Com]]:=TDLlsb; { in order to access divisor }
- port[ComBase[Com]+1]:=TDLmsb; { latches, then store values }
- port[ComBase[Com]+LineCtrl]:=Tlcr and $7F;{ clear the DLAB }
- inline($FB); { interupts on }
- CLEAR(InputBuffer[Com]); { clear the buffers }
- CLEAR(OutputBuffer[Com]);
- INTON(Com); { establish our vectors }
- TXCONTROL(Com,true); { assert DTR }
- end; {INITCOM}
-
- procedure TERMINATECOM(Com:byte);
-
- var
- TByte:byte;
-
- begin
- TXCONTROL(Com,false);
- inline($FA); { CLI } { Disable interrupts }
- TByte:=port[$21]; { }
- port[ComBase[Com]+IntenReg]:=0; { Disable COM interrupts }
- if Com=1 then port[$21]:=TByte or $10 { turn off interrupt control}
- else port[$21]:=TByte or $08;
- memw[0000:irq4]:=ComVecOff[Com]; { Restore the DOS interrupt }
- memw[0000:irq4+2]:=ComVecSeg[Com]; { handler }
- ComBusy[Com]:=true;
- inline($FB);
- end;
-
- function BUFFERFULL(var Buf:BufferType):boolean;
-
- { this fuction tests to see it the queue is full }
-
- var Temp:integer;
-
- begin
- Temp:=(Buf.Head+1) mod sizeof(Buf.Data);
- BufferFull:=(Temp=Buf.Tail);
- end;
-
- function BUFFEREMPTY(var Buf:BufferType):boolean;
-
- { this function test to see if the queue is empty }
-
- begin
- BUFFEREMPTY:=Buf.Tail=Buf.Head;
- end;
-
- procedure WRITEBUFFER(var Buf:BufferType;var Input:char);
-
- { this procedure adds an entry to the queue at the rear }
-
- var Temp:integer;
-
- begin
- if BUFFERFULL(Buf) then Buf.Over:=true {overflow condition }
- else begin { there is room}
- Temp:=(Buf.Head+1) mod sizeof(Buf.Data);
- Buf.Data[Temp]:=ord(Input);
- Buf.Head:=Temp;
- end;
- end; {BUFFERWRITE}
-
- function READBUFFER(var Buf:BufferType):byte;
-
- { this procedure removes an entry from the queue at the front }
-
- begin
- Buf.Tail:=(Buf.Tail+1) mod sizeof(Buf.Data);
- ReadBuffer:=Buf.Data[Buf.Tail];
- end;
-
- procedure SENDSTRING(Com:byte;S:WorkLineType);
-
- { this is the main output routine, which sends a string }
-
- var I,J:byte;
-
- begin
- J:=ord(S[0]); { get length of string }
- for I:=1 to J do WRITEBUFFER(OutputBuffer[Com],S[I]); {output to buf}
- if not(ComBusy[Com]) and not(BUFFEREMPTY(OutputBuffer[Com]))
- then begin { tickle the transmitter to start output}
- ComBusy[Com]:=true;
- port[ComBase[Com]]:=READBUFFER(OutputBuffer[Com]);
- end;
- end; {SENDSTRING}
-
- function DCDSTATUS(Com:byte):boolean;
-
- { Returns the stats of the carier detect line }
-
- begin {DCDSTATUS}
- with RegisterSet do begin
- AH:=$03;
- DX:=pred(Com);
- intr($14,RegisterSet);
- DCDStatus:=(AL and $80)=$80;
- end;
- end; {DCDSTATUS}
-
- function CTSSTATUS(Com:byte):boolean;
-
- { returns the status of the clear to send line}
-
- begin {CTSSTATUS}
- with RegisterSet do begin
- AH:=$03;
- DX:=pred(Com);
- intr($14,RegisterSet);
- CTSStatus:=(AL and $10)=$10;
- end;
- end; {CTSSTATUS}