(* 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 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 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 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 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 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 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;