UNIT WCom;
{ -- Asychronous communications routines for Turbo Pascal --
Copyright (c) 1995 by Wayne Hoxsie. Revision date 5-27-95
You may use this unit as you please as long as you mention
me in your documentation.
}
INTERFACE
PROCEDURE AsyncTransmit(ch1:char);
FUNCTION AsyncRecieve:char;
PROCEDURE AsyncFlushComm;
FUNCTION AsyncCharWaiting:BOOLEAN;
FUNCTION AsyncCDetect:boolean;
PROCEDURE AsyncInit(port_num:byte);
PROCEDURE AsyncShutdown;
IMPLEMENTATION
USES DOS;
CONST
(****************************************************************************
These are the common base addresses and IRQ's for the 4 COM ports. These
will need to be edited if you use non-standard addresses or IRQ's
*)
port_base_id : array[0..4] of word = (0,$3f8,$2f8,$2e8,$3e8);
port_IRQ : array[0..4] of byte = (0,4,3,4,3);
(****************************************************************************
These are jusr the mneumonics for the UART registers which are accesses
relative to the com port's base address.
*)
IER=1;
FCR=2;
IIR=2;
LCR=3;
MCR=4;
LSR=5;
MSR=6;
VAR
(****************************************************************************
The head and tail are mearly to keep track of how many characters are in
the buffer. If they are not equal, then there are characters waiting to
be processed. Since it is a circular buffer, there may be characters still
in the buffer which have already been processed.
*)
buf_head,buf_tail : word;
com_buf : array[0..1024] of byte;
(****************************************************************************
These are the variables which will hold the current com port info.
*)
async_irq,port_base : word;
(****************************************************************************
These are just the ASM CLI and RTI instructions used to prevent another
interrupt during the processing of data directly related to the interrupt
handler.
*)
PROCEDURE Disable; INLINE($FA);
PROCEDURE Enable; INLINE($FB);
(****************************************************************************
This is the actual interrupt handler. When a character is recieved by the
com port, it signals the 8259 PIC which flags the processor to run the
following code. It grabs the character from the UART register and puts it
into our circular buffer to be processed by the program at a later time.
This is the essence of "Interrupt Driven Communications."
*)
{$F+} { Make it a FAR function }
PROCEDURE async_isr; INTERRUPT;
BEGIN
com_buf[buf_head]:=port[port_base];
inc(buf_head);
IF buf_head = 1024 THEN
buf_head := 0;
port[$20]:=$20;
END;
{$F-}
(****************************************************************************
This procedure mearly sends a character out the com port.
*)
PROCEDURE AsyncTransmit(ch1:CHAR);
VAR
temp : BYTE;
BEGIN
(*
First we poll the LSR until there are no characters waiting in the
transmitter holding register. The bit we check (bit 5) is the THRE
(Transmit Holding Register Empty) bit.
*)
repeat
temp:=port[port_base + LSR] AND $20;
until temp<>0;
(*
Now we can place a character in the register for the UART to send out.
*)
port[port_base]:=integer(ch1);
END;
(****************************************************************************
This function just checks to see if a character is waiting to be processed.
It doesn't effect the character at all.
*)
FUNCTION AsyncCharWaiting:BOOLEAN;
BEGIN
asynccharwaiting := buf_head<>buf_tail;
END;
(****************************************************************************
This function pulls a character out of our buffer and increments the
pointers appropriately so that the interrupt handler can put the next
character at the right place. If no character is available, then we
return a 0.
*)
FUNCTION AsyncRecieve:char;
VAR
c1 : char;
BEGIN
(* First make sure a char is waiting *)
if buf_head <> buf_tail then
BEGIN
disable;
c1 := char(com_buf[buf_tail]);
inc(buf_tail);
if buf_tail = 1024 then
buf_tail := 0;
enable;
asyncrecieve:=c1;
END else
asyncrecieve:=#0;
END;
(****************************************************************************
This flushes all characters out of the buffer and resets the pointers
*)
PROCEDURE AsyncFlushComm;
BEGIN
disable;
buf_head :=0;
buf_tail :=0;
enable;
END;
(****************************************************************************
This procedure sets the DTR pin to the desired value--ON or OFF--to tell
the program on the other end that we are ready or not-ready.
*)
PROCEDURE dtr(i : BOOLEAN);
VAR
i1 : word;
BEGIN
i1 := port[port_base + MCR] AND $00FE;
if i then
port[port_base + MCR]:= i1+1 else
port[port_base + MCR] := i1;
END;
(****************************************************************************
This tells us if a carrier is detected or not.
*)
FUNCTION AsyncCDetect:boolean;
BEGIN
asynccdetect := (port[port_base + MSR] AND $0080)<> 0;
END;
(****************************************************************************
This is the procedure that must be called to set up our com port for
operation. This installs the interrupt and sets the registers to the
desired values.
*)
PROCEDURE AsyncInit(port_num : byte);
VAR
temp : word;
BEGIN
port_base := port_base_id[port_num];
async_irq := port_IRQ[port_num];
setintvec(8 + async_irq, addr(async_isr));
buf_head :=0;
buf_tail := 0;
port[port_base + LCR]:= $03; { set N81 parameters }
disable;
temp := port[port_base + LSR]; { Read LSR to reset all bits }
temp := port[port_base]; { clear any character still pending }
temp := port[$21]; { read the PIC mask }
temp := temp AND((1 SHL async_irq)XOR $00FF); {set appropriate bit }
port[$21]:=temp; {set the PIC mask appropriately}
port[port_base + IER]:=1;
temp:=port[port_base + MCR];
port[port_base + MCR]:= temp OR $0A; {Set MCR }
port[port_base + FCR]:=$c0; {enable FIFO's on 16550A's }
enable;
dtr(TRUE); {tell the world, "We're ready!" }
END;
(****************************************************************************
This resets all the stuff we set up in the first place.
*)
PROCEDURE AsyncShutdown;
VAR
temp : integer;
oldvec : pointer;
BEGIN
if port_base<>0 then BEGIN
disable;
temp := port[$21];
temp := temp OR ((1 SHL async_irq)); { unmask the PIC }
port[$21]:= temp;
port[port_base + IIR]:=0; { shutdown the interrupts }
port[port_base + MCR]:=3;
getintvec(8,oldvec);
setintvec(async_irq+8,oldvec);
enable;
port_base:=0;
END;
END;
END.
{End of Unit==Cut here}
program dumb_terminal;
uses dos,wcom,crt;
var
temp : char;
done : boolean;
i,code : integer;
begin
if(paramcount<1) then
i:=1
else
val(paramstr(1),i,code);
done:=false;
asyncinit(2);
repeat begin
if keypressed then begin
temp:=readkey;
if temp=#27 then done:=true;
asynctransmit(temp);
end;
if asynccharwaiting then
write(asyncrecieve);
end;
until done;
asyncshutdown;
end.
(
geocities.com/~franzglaser)