Category : Pascal Source Code
Archive   : PASS_ALL.ZIP
Filename : TI226.ASC

 
Output of file : TI226.ASC contained in archive : PASS_ALL.ZIP









PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 1/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




This version of Michael Quinlan's ASYNC.INC is compatible with
IBM PC and Compatibles. It gives interrupt-driven buffered
communication capabilities to Turbo programs written for the IBM
PC. It is heavily dependent on that hardware.

**NOTE: Pages 12 through 15 contain an additional routine that
allows your program to change the communications parameters.

The following example routines are public domain programs that
have been uploaded to our Forum on CompuServe. As a courtesy to
our users that do not have immediate access to CompuServe,
Technical Support distributes these routines free of charge.

However, because these routines are public domain programs, not
developed by Borland International, we are unable to provide any
technical support or assistance using these routines. If you need
assistance using these routines, or are experiencing
difficulties, we recommend that you log onto CompuServe and
request assistance from the Forum members that developed these
routines.

{--------------------------------------------------------------}
{ ASYNC.INC }
{ }
{ Async Communication Routines }
{ by Michael Quinlan }
{ with a bug fixed by Scott Herr }
{ made PCjr-compatible by W. M. Miller }
{ Highly dependent on the IBM PC and PC DOS 2.0 }
{ }
{ based on the DUMBTERM program by CJ Dunford }
{ in the January 1984 }
{ issue of PC Tech Journal. }
{ }
{ Entry points: }
{ Async_Init }
{ Performs initialization. }
{ }
{ Async_Open(Port, Baud : Integer; }
{ Parity : Char; }
{ WordSize, StpBits : Integer) : Boolean }
{ Sets up interrupt vector, initialize the COM port for }
{ processing, sets pointers to the buffer. Returns FALSE }














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 2/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




{ if COM }
{ port not installed. }
{ }
{ Async_Buffer_Check(var C : Char) : Boolean }
{ If a character is available, returns TRUE and moves the }
{ character from the buffer to the parameter }
{ Otherwise, returns FALSE }
{ }
{ Async_Send(C : Char) }
{ Transmits the character. }

{ Async_Send_String(S : LStr) }
{ Calls Async_Send to send each character of S. }
{ Async_Close }
{ Turn off the COM port interrupts. }
{ **MUST** BE CALLED BEFORE EXITING }
{ YOUR PROGRAM; otherwise you }
{ will see some really strange errors and have to re-boot. }
{--------------------------------------------------------------}
{ global declarations }

type
LStr = String[255]; { generic string type for parameters }

const
UART_THR = $00;
{ offset from base of UART Registers for IBM PC }
UART_RBR = $00;
UART_IER = $01;
UART_IIR = $02;
UART_LCR = $03;
UART_MCR = $04;
UART_LSR = $05;
UART_MSR = $06;

I8088_IMR = $21;
{ port address of the Interrupt Mask Register }

const
Async_DSeg_Save : Integer = 0;
{ Save DS reg in Code Segment
for interrupt routine }















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 3/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




const
Async_Buffer_Max = 4095;

var
Async_Buffer : Array[0..Async_Buffer_Max] of char;
Async_Open_Flag : Boolean;
Async_Port : Integer; { current Open port number (1 or 2) }
Async_Base : Integer; { base for current open port }
Async_Irq : Integer; { irq for current open port }

Async_Buffer_Overflow : Boolean;
{ True if buffer overflow has happened }
Async_Buffer_Used : Integer;
Async_MaxBufferUsed : Integer;

{ Async_Buffer is empty if Head = Tail }
Async_Buffer_Head : Integer;
{ Locn in Async_Buffer to put next char }
Async_Buffer_Tail : Integer;
{ Locn in Async_Buffer to get next char }
Async_Buffer_NewTail : Integer;

Async_BIOS_Port_Table : Array[1..2] of Integer absolute $40:0;
{ This table is initialized by BIOS equipment determination
code at boot time to contain the base addresses for the
installed async adapters. A value of 0 means "not in-
stalled." }

const
Async_Num_Bauds = 8;
Async_Baud_Table : array [1..Async_Num_Bauds] of record
Baud, Bits : integer
end
= ((Baud:110; Bits:$00),
(Baud:150; Bits:$20),
(Baud:300; Bits:$40),
(Baud:600; Bits:$60),
(Baud:1200; Bits:$80),
(Baud:2400; Bits:$A0),
(Baud:4800; Bits:$C0),
(Baud:9600; Bits:$E0));

procedure BIOS_RS232_Init(ComPort, ComParm : Integer);














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 4/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




{ Issue Interrupt $14 to initialize the UART }
{ See the IBM PC Technical Reference Manual
for the format of ComParm }
var
Regs : record
ax, bx, cx, dx, bp, si, di, ds, es, flag : Integer
end;
begin
with Regs do
begin
ax := ComParm and $00FF; { AH=0; AL=ComParm }
dx := ComPort;
Intr($14, Regs)
end
end; { BIOS_RS232_Init }

procedure DOS_Set_Intrpt(v, s, o : integer);
{ call DOS to set an interrupt vector }
var
Regs : Record
ax, bx, cx, dx, bp, si, di, ds, es, flag : integer
end;
begin
with Regs do
begin
ax := $2500 + (v and $00FF);
ds := s;
dx := o;
MsDos(Regs)
end
end; { DOS_Set_Intrpt }

{----------------------------------------------------------}
{ }
{ ASYNCISR.INC - Interrupt Service Routine }
{ }
{----------------------------------------------------------}

procedure Async_Isr;
{ Interrupt Service Routine }
{ Invoked when the UART has received a byte of data from the
communication line }















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 5/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




{ re-written 9/14/84 to be entirely in machine language;
original source left as comments }

begin

{NOTE: on entry, Turbo Pascal has already PUSHed BP and SP }
Inline(
{ save all registers used }
$50/ { PUSH AX }
$53/ { PUSH BX }
$52/ { PUSH DX }
$1E/ { PUSH DS }
$FB/ { STI }
{ set up the DS register to point to Turbo Pascal's data segment }
$2E/$FF/$36/Async_Dseg_Save/ { PUSH CS:Async_Dseg_Save }
$1F/ { POP DS }
{ get the incoming character }
{ Async_Buffer[Async_Buffer_Head]
:= Chr(Port[UART_RBR + Async_Base]); }
$8B/$16/Async_Base/ { MOV DX,Async_Base }
$EC/ { IN AL,DX }
$8B/$1E/Async_Buffer_Head/ { MOV BX,Async_Buffer_Head }
$88/$87/Async_Buffer/ { MOV Async_Buffer[BX],AL }
{ Async_Buffer_NewHead := Async_Buffer_Head + 1; }
$43/ { INC BX }
{ if Async_Buffer_NewHead > Async_Buffer_Max then
Async_Buffer_NewHead := 0; }
$81/$FB/Async_Buffer_Max/ { CMP BX,Async_Buffer_Max }
$7E/$02/ { JLE L001 }
$33/$DB/ { XOR BX,BX }
{ if Async_Buffer_NewHead = Async_Buffer_Tail then
Async_Buffer_Overflow := TRUE
else }
{L001:}
$3B/$1E/Async_Buffer_Tail/ { CMP BX,Async_Buffer_Tail }
$75/$08/ { JNE L002 }
$C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
$90/ { NOP generated by assembler
for some reason }
$EB/$16/ { JMP SHORT L003 }
{ begin
Async_Buffer_Head := Async_Buffer_NewHead;
Async_Buffer_Used := Async_Buffer_Used + 1;














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 6/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




if Async_Buffer_Used > Async_MaxBufferUsed then
Async_MaxBufferUsed := Async_Buffer_Used
end; }
{L002:}
$89/$1E/Async_Buffer_Head/ { MOV Async_Buffer_Head,BX }
$FF/$06/Async_Buffer_Used/ { INC Async_Buffer_Used }
$8B/$1E/Async_Buffer_Used/ { MOV BX,Async_Buffer_Used }
$3B/$1E/Async_MaxBufferUsed/ { CMP BX,Async_MaxBufferUsed }
$7E/$04/ { JLE L003 }
$89/$1E/Async_MaxBufferUsed/ { MOV Async_MaxBufferUsed,BX }
{L003:}
{ disable interrupts }
$FA/ { CLI }
{ Port[$20] := $20; } { use non-specific EOI }
$B0/$20/ { MOV AL,20h }
$E6/$20/ { OUT 20h,AL }
{ restore the registers then use IRET to return }
{ the last two POPs are required because Turbo Pascal
PUSHes these regs before we get control. The manual
doesn't so it, but that is what really happens }
$1F/ { POP DS }
$5A/ { POP DX }
$5B/ { POP BX }
$58/ { POP AX }
$5C/ { POP SP }
$5D/ { POP BP }
$CF) { IRET }
end; { Async_Isr }

procedure Async_Init;
{ initialize variables }
begin
Async_DSeg_Save := DSeg;
Async_Open_Flag := FALSE;
Async_Buffer_Overflow := FALSE;
Async_Buffer_Used := 0;
Async_MaxBufferUsed := 0;
end; { Async_Init }

procedure Async_Close;
{ reset the interrupt system when UART interrupts
no longer needed }
var














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 7/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




i, m : Integer;
begin
if Async_Open_Flag then
begin

{ disable the IRQ on the 8259 }
Inline($FA); { disable interrupts }
i := Port[I8088_IMR];
{ get the interrupt mask register }
m := 1 shl Async_Irq;
{ set mask to turn off interrupt }
Port[I8088_IMR] := i or m;

{ disable the 8250 data ready interrupt }
Port[UART_IER + Async_Base] := 0;

{ disable OUT2 on the 8250 }
Port[UART_MCR + Async_Base] := 0;
Inline($FB); { enable interrupts }

{ re-initialize our data areas so
we know the port is closed }
Async_Open_Flag := FALSE

end
end; { Async_Close }

function Async_Open(ComPort : Integer;
BaudRate : Integer;
Parity : Char;
WordSize : Integer;
StopBits : Integer) : Boolean;
{ open a communications port }
var
ComParm : Integer;
i, m : Integer;
begin
if Async_Open_Flag then Async_Close;

if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
Async_Port := 2
else
Async_Port := 1; { default to COM1 }














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 8/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




Async_Base := Async_BIOS_Port_Table[Async_Port];
Async_Irq := Hi(Async_Base) + 1;

if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
Async_Open := FALSE
else
begin
Async_Buffer_Head := 0;
Async_Buffer_Tail := 0;
Async_Buffer_Overflow := FALSE;

{ Build the ComParm for RS232_Init }
{ See Technical Reference Manual for description }

ComParm := $0000;

{ Set up the bits for the baud rate }
i := 0;
repeat
i := i + 1
until (Async_Baud_Table[i].Baud = BaudRate)
or (i = Async_Num_Bauds);
ComParm := ComParm or Async_Baud_Table[i].Bits;

if Parity in ['E', 'e'] then ComParm := ComParm or $0018
else if Parity in ['O', 'o'] then
ComParm := ComParm or $0008
else ComParm := ComParm or $0000; { default to No parity }

if WordSize = 7 then ComParm := ComParm or $0002
else ComParm := ComParm or $0003; { default to 8 data bits }

if StopBits = 2 then ComParm := ComParm or $0004
else ComParm := ComParm or $0000; { default to 1 stop bit }

{ use the BIOS COM port initialization routine
to save typing the code }

BIOS_RS232_Init(Async_Port - 1, ComParm);

DOS_Set_Intrpt(Async_Irq + 8, CSeg, Ofs(Async_Isr));

{ read the RBR and reset any possible pending error conditions














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 9/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




first turn off the Divisor Access Latch Bit to allow
access to RBR, etc. }

Inline($FA); { disable interrupts }

Port[UART_LCR + Async_Base] :=
Port[UART_LCR + Async_Base] and $7F;
{ read the Line Status Register to reset any
errors it indicates }
i := Port[UART_LSR + Async_Base];
{ read the Receiver Buffer Register in case
it contains a character }
i := Port[UART_RBR + Async_Base];

{ enable the irq on the 8259 controller }
i := Port[I8088_IMR]; { get the interrupt mask register }
m := (1 shl Async_Irq) xor $00FF;
Port[I8088_IMR] := i and m;

{ enable the data ready interrupt on the 8250 }
Port[UART_IER + Async_Base] := $01;
{ enable data ready interrupt }

{ enable OUT2 on 8250 }
i := Port[UART_MCR + Async_Base];
Port[UART_MCR + Async_Base] := i or $08;
Inline($FB); { enable interrupts }
Async_Open_Flag := TRUE; { bug fix by Scott Herr }
Async_Open := TRUE
end
end; { Async_Open }

function Async_Buffer_Check(var C : Char) : Boolean;
{ see if a character has been received; return it if yes }
begin
if Async_Buffer_Head = Async_Buffer_Tail then
Async_Buffer_Check := FALSE
else
begin
C := Async_Buffer[Async_Buffer_Tail];
Async_Buffer_Tail := Async_Buffer_Tail + 1;
if Async_Buffer_Tail > Async_Buffer_Max then
Async_Buffer_Tail := 0;














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 10/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




Async_Buffer_Used := Async_Buffer_Used - 1;
Async_Buffer_Check := TRUE
end
end; { Async_Buffer_Check }

procedure Async_Send(C : Char);
{ transmit a character }
var
i, m, counter : Integer;
begin
Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }

{ wait for CTS }
counter := MaxInt;
while (counter <> 0) and
((Port[UART_MSR + Async_Base] and $10) = 0) do
counter := counter - 1;

{ wait for Transmit Hold Register Empty (THRE) }
if counter <> 0 then counter := MaxInt;
while (counter <> 0) and
((Port[UART_LSR + Async_Base] and $20) = 0) do
counter := counter - 1;
if counter <> 0 then
begin
{ send the character }
Inline($FA); { disable interrupts }
Port[UART_THR + Async_Base] := Ord(C);
Inline($FB) { enable interrupts }
end
else
writeln('<<>>');
end; { Async_Send }

procedure Async_Send_String(S : LStr);
{ transmit a string }
var
i : Integer;
begin
for i := 1 to length(S) do
Async_Send(S[i])
end; { Async_Send_String }















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 11/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




________________________________________________________________

CHANGE PARAMETERS
________________________________________________________________


{ ASYCHG.INC - procedure to change communication parameters }
{ "on the fly" must be Included following ASYNC.INC }

procedure Async_Change(BaudRate : Integer;
Parity : Char;
WordSize : Integer;
StopBits : Integer);
{ change communication parameters "on the fly" }
{ you cannot use the BIOS routines because they drop DTR }

const num_bauds = 15;
divisor_table : array [1..num_bauds] of record
baud, divisor : integer
end
= ((baud:50; divisor:2304),
(baud:75; divisor:1536),
(baud:110; divisor:1047),
(baud:134; divisor:857),
(baud:150; divisor:768),
(baud:300; divisor:384),
(baud:600; divisor:192),
(baud:1200; divisor:96),
(baud:1800; divisor:64),
(baud:2000; divisor:58),
(baud:2400; divisor:48),
(baud:3600; divisor:32),
(baud:4800; divisor:24),
(baud:7200; divisor:16),
(baud:9600; divisor:12));

var i : integer;
dv : integer;
lcr : integer;

begin

{ Build the Line Control Register and find














PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 12/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




the divisor (for the baud rate) }

{ Set up the divisor for the baud rate }
i := 0;
repeat
i := i + 1
until (Divisor_Table[i].Baud = BaudRate) or (i = Num_Bauds);
dv := Divisor_Table[i].divisor;

lcr := 0;
case Parity of
'E' : lcr := lcr or $18; { even parity }
'O' : lcr := lcr or $08; { odd parity }
'N' : lcr := lcr or $00; { no parity }
'M' : lcr := lcr or $28; { Mark parity }
'S' : lcr := lcr or $38; { Space parity }
else
lcr := lcr or $00; { default to no parity }
end;

case WordSize of
5 : lcr := lcr or $00;
6 : lcr := lcr or $01;
7 : lcr := lcr or $02;
8 : lcr := lcr or $03;
else
lcr := lcr or $03; { default to 8 data bits }
end;

if StopBits = 2 then lcr := lcr or $04
else lcr := lcr or $00; { default to 1 stop bit }

lcr := lcr and $7F; { make certain the DLAB is off }

Inline($FA); { disable interrupts }

{ turn on DLAB to access the divisor }
Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] or $80;

{ set the divisor }
Port[Async_Base] := Lo(dv);
Port[Async_Base + 1] := Hi(dv);















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS

DATE : August 1, 1986 PAGE : 13/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




{ turn off the DLAB and set the new comm. parameters }
Port[UART_LCR + Async_Base] := lcr;

Inline($FB); { enable interrupts }

end; { Async_Change }

The following are two example programs which use ASYNC.INC.

program DumbTerminal;
{$C-}
{$I ASYNC.INC}
var
ch: Char;
stop: Boolean;
begin
stop := false;
Async_Init;
if not Async_Open(1, 1200, 'E', 7, 1) then
begin
writeln('Invalid port');
Halt
end;
LowVideo;
writeln('COM1 now open at 1200 baud, 7 data bits, even parity, ',
'1 stop bit.');
write('All keyboard input will be sent out COM1');
writeln('all input from COM1');
writeln('will be displayed on the screen. To quit, type ^Z.');
writeln;
repeat
if Async_Buffer_Check(ch) then write(ch);
if KeyPressed then
begin
read(Kbd, ch);
if ch = ^Z then stop := true else Async_Send(ch)
end
until stop;
Async_Close
end.

program tty;















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 14/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




{$IASYNC.INC}

var
c : char;

begin
Async_Init; { initialize variables }
if not Async_Open(1, 1200, 'E', 7, 1) then {open communications
port}
begin
writeln('**ERROR: Async_Open failed');
halt
end;

writeln('TTY Emulation begins now...');
writeln('Press any function key to terminate...');

repeat
if Async_Buffer_Check(c) then
case c of
#000 : ; { strip incoming nulls }
#010 : ; { strip incoming line feeds }
#012 : ClrScr; { clear screen on a form feed }
#013 : Writeln { handle carriage return as CR/LF }
else
write(c) { else write incoming char to the screen }
end; { case }
if KeyPressed then
begin
Read(Kbd, c);
if c = #027 then { handle IBM Extended Ascii codes }
begin
Read(Kbd, c); { clear the rest of the extended code }
Async_Close; { reset the interrupt system, etc. }
Writeln('End of TTY Emulation...');
Writeln('Max Buffer Used = ', Async_MaxBufferUsed);
halt { terminate the program }
end
else
Async_Send(c)
end
until FALSE















PRODUCT : TURBO PASCAL NUMBER : 226
VERSION : 3.0xx
OS : PC-DOS
DATE : August 1, 1986 PAGE : 15/15
TITLE : ASYNCHRONOUS COMMUNICATIONS




end.