(* These drivers were adapted from routines written by Tim Shimeall
   for a PCNET implementation, based on information from Western
   Digital.

   On the Microengine, there are two RS232C Serial Ports.  Port A is
   reserved for the system terminal.  Port B is available for all other
   devices which may be desired to hang off a Microengine.  In this code,
   it is assumed that Port B holds the modem.
*)
(* All functions are duplicated on ports A and B for simplicity *)

PROCEDURE Init;
BEGIN (* InitM *)
   PortB.DevAdd:= Channel0;
   PortA.DevAdd:= Channel1;
   WITH PortB.Serial^ DO BEGIN
     {The following two lines set the serial port to the following
      commands:
         Control1:
           1 - Full Duplex Operation
           0 - Break or Transmit NOT transparent
           0 - Send 2 stop bits on Transmitted 8-bit data
           0 - No echo of Recieved data
           0 - Parity checking/generation OFF
           1 - Reciever is enabled (chars in Rec. holding reg.)
           1 - REQUEST TO SEND is enabled if CTS is low
           1 - DTR is ON
          Control2:
           0 - 8 bits
           0 - 8 bits
           0 - Asynchronous character mode
           0 - even parity
           0 - select reciever rate 1
           0 - +
           0   | - Clock select to rate 1 (32X)
           1 - +
       }
     Control1:=135; {87 hex}
     Control2:=1;
     END;
   WITH PortA.Serial^ DO BEGIN
     Control1:=135;
     Control2:=1;
     END;
END; (*InitM*)

(*---------------------UART FLAG CHECKING-------------------------------*)

function ISTARR(*:boolean *);
(* ARR -- IS True Port A Receive Ready?

This checks the UART status bit corresponding to Receive Data
Available. If data is available a true result is returned.*)
BEGIN
  ISTARR:=PortA.Serial^.StatSynDle.status[DataReceived];
END;

function ISTBRR(*:boolean*);
(* BRR -- IS True Port B Receive Ready?*)
BEGIN
  ISTBRR:=PortB.Serial^.StatSynDle.status[DataReceived];
END;

function ISTAOR(*:boolean*);
(* AOR -- IS it True that data OverRun occurred?:0 istor<ditto>

Immediately after RCVBT is called, ISTOR may be called to check for
data overrun. This function isn't necessary, but it helps diagnose
software that is losing data because it is too slow to receive data
before that data starts getting shifted out of the way to make way for
later data that has already started to arrive.
*)
BEGIN
  ISTAOR:=PortA.Serial^.StatSynDle.Status[OverError];
END;

function ISTBOR(*:boolean*);
BEGIN
  ISTBOR:=PortB.Serial^.StatSynDle.Status[OverError];
END;

function ISTAFE(*:boolean *);
(* FE -- IS it True that Framing-Error occurred?:0 istfe<ditto>

Immediately after RCVBT is called, ISTFE may be called to check for
framing error. This function isn't necessary, but it helps diagnose
various errors such as phone-line-noise and wrong-speed-UART. Normally
ISTOR will be called before ISTFE since data overrun is a more serious
error than framing-error and thus pre-empts framing-error. The entire
sequence is thus: ISTRR, RCVBT, ISTOR, ISTFE.
*)
BEGIN
  ISTAFE:=PortA.Serial^.StatSynDle.Status[FrameError];
END;

function ISTBFE(*:boolean*);
BEGIN
  ISTBFE:=PortB.Serial^.StatSynDle.Status[FrameError];
END;

function ISTATR(*:boolean *);
(* TR -- IS it True that Transmit is Ready?:0 isttr<used in FDX&SDWBT>

ISTTR is analagous to ISTRR, it tells whether it's safe to transmit
(rather than to receive) a byte of data. Internally it tells whether
the previous byte has cleared the device so that the buffer is empty
to accept another byte. In the device descripion it's usually called
Transmit Buffer Empty. For instantaneous devices such as memory-mapped
CRTs, this function will always return TRUE. For most other devices
such as UARTs and ACIAs (connected directly to terminals, or to
modems), ISTTR will return TRUE initially, then return FALSE as soon
as a byte is sent to the device, and then return TRUE when actual
transmission is done. For double-buffered devices it may only go FALSE
only after two characters are sent to it, one of which is actually en
route and the other of which is merely occupying the extra buffer.
*)
BEGIN
  ISTATR:=PortA.Serial^.StatSynDle.Status[RegEmpty];
END;

function ISTBTR(*:boolean*);
BEGIN
  ISTBTR:=PortB.Serial^.StatSynDle.Status[RegEmpty];
END;

(*------------------Primitive character sending and receiving---------------*)

function RCVABT(*:CHAR*) ;
(* ReCeiVe ByTe of data from device:0 rcvbt<used in FDX and RCWBT>

This is the function that is called after ISTRR returns true, to
actually fetch the waiting data from the UART or ACIA into the
computer, freeing the device to accept the next byte of data. These
two functions, testing for data ready and actually fetching the data,
are kept separate for two reasons: (1) they are separate hardware
functions in most existing devices, ISTRR being a read of the status
port with testing for a bit and RCVBT being a read of the data
port, and (2) often they must be separate in the software, such as
when it's necessary to verify both that data is available and there's
a place to put it before fetching the data, such as in a terminal emulator.

Note that calling RCVBT any time other than after getting a true
result from ISTRR is invalid, yielding random garbage such as part of
an incoming byte shifted. Note also that RCVBT fetches all 8 bits of
the incoming byte of data, returning an 8-bit number with each bit in
its normal position, for example the first-arrived bit is the 1 bit,
then the 2 bit, etc., with the "parity" bit which is the last-arrived
appearing simply as an 8th bit (hexadecimal value 80). No checking of
parity is allowed, nor is stripping off of the parity bit. When only 7
bits are desired, a higher-level function will strip off the parity bit.*)
BEGIN
  RCVABT:=CHR(PortA.Serial^.SerData);
END;

function RCVBBT(*:CHAR*);
BEGIN
  RCVBBT:=CHR(PortB.Serial^.SerData);
END;

procedure SNDABT(* (BT:CHAR)*);
(* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>

After getting back a TRUE result from isttr, this function SNDBT is
used to actually send the byte of data from the CPU to the device, so
as to effect sending it out the I/O port (modem or local CRT). Note
that any attempt to call SNDBT without first getting TRUE from isttr
can result in clobbering previous data that is still in transit from
the UART or ACIA bit by bit, causing both that previous byte and this
new byte to be lost/garbaged. *)
BEGIN (* SNDABT*)
  PortA.Serial^.SerData:=ORD(BT);
END(*SNDABT*);

procedure SNDBBT(* (BT:CHAR)*);
(* SeND ByTe of data:0 sndbt<used in FDX&SDWBT>

After getting back a TRUE result from isttr, this function SNDBT is
used to actually send the byte of data from the CPU to the device, so
as to effect sending it out the I/O port (modem or local CRT). Note
that any attempt to call SNDBT without first getting TRUE from isttr
can result in clobbering previous data that is still in transit from
the UART or ACIA bit by bit, causing both that previous byte and this
new byte to be lost/garbaged. *)
BEGIN (* SNDBBT*)
  PortB.Serial^.SerData:=ORD(BT);
END(*SNDBBT*);
 
procedure finit;
BEGIN
  PortB.Serial^.Control1:=0; {Turn off DTR, which causes modem to hang up}
END;


