{ low level 16C450 + 16C550 type serial port routines } const Com1 =$03F8; Com2 =$02F8; Com3 =$03E8; Com4 =$02E8; RecReg =$00; TrHReg =$00; DivLatLSB =$00; DivLatMSB =$01; IntEn =$01; IntId =$02; FIFOCont =$02; LineCont =$03; ModemCont =$04; LineStat =$05; ModemStat =$06; Scratch =$07; StdBRClock = 1.8432E6; { IBM's dumb rate } HiBRClock = 8.0000E6; { for newer 16C550's } RealHIBRClock = 2.4000E7; { for some 16C552's ... Wheee! } DoubleBRClock = 3.6864E6; QuadBRClock = DoubleBRClock*2; { Line control register stuff } WLen5 =$00; WLen6 =$01; WLen7 =$02; WLen8 =$03; StopBits =$04; ParityEn =$08; EvenParity =$10; StickParity=$20; SetBreak =$40; DLAB =$80; NoDLAB =$7F; DefaultLCR = 0 or WLen8; { Line status register stuff } DataReady =$01; OverRunErr =$02; ParityErr =$04; FrameErr =$08; BreakInt =$10; THRE =$20; TSRE =$40; { Interrupt ID register stuff } { read masks } IntPending =$01; RecChar =$04; XmitRdy =$02; RecErr =$06; { FIFO control register stuff } FIFOEnable =$01; RecvFIFORst =$02; XmitFIFORst =$04; DMAMode =$08; RecvTrig1 =$00; RecvTrig4 =$40; RecvTrig8 =$80; RecvTrig14 =$C0; FIFOsOn =$C0; { Interrupt enable register stuff } DAvIntEn =$01; TXRdyIntEn =$02; RXErrIntEn =$04; LineChIntEn =$08; DefaultIER = 0; { Modem control register stuff } DTRBit =$01; RTSBit =$02; Out1Bit =$04; Out2Bit =$08; LoopBack =$10; defaultMCR = 0 or DTRBit or RTSBit or Out1Bit; { Modem status register stuff } DelCTS =$01; DelDSR =$02; DelRI =$04; DelCD =$08; CTSBit =$10; DSRBit =$20; RIBit =$40; CDBit =$80; type PortType =(CP8250,CP16450,CP16550); SerialSetupType = record BaudRate : real; IntEnImage : byte; LineContImage : byte; ModemContImage : byte; end; FifteenCharFIFO = record PutPointer : word; GetPointer : word; DataBuffer : array [0..15] of byte; ErrorBuffer : array [0..15] of byte; end; function BRDivisorFromBaud(BRC : real;baudrate : longint) : word; begin BRDivisorFromBaud := word(trunc(STDBRClock/(baudrate*16)+0.5)); end; procedure SetBRDivisor(comPort : word; divisor : word); var portSave : byte; begin portSave := port[comPort + LineCont]; port[comPort + LineCont] := portSave or DLAB; port[comPort + DivLatLSB] := lo(divisor); port[comPort + DivLatMSB] := hi(divisor); port[comPort + LineCont] := portSave and NoDLAB; end; procedure GetBRDivisor(comPort : word; var divisor : word); var portSave : byte; begin portSave := port[comPort + LineCont]; port[comPort + LineCont] := portSave or DLAB; divisor := port[comPort + DivLatLSB]; divisor := divisor or (port[comPort + DivLatMSB] * $0100); port[comPort + LineCont] := portSave and NoDLAB; end; procedure SetOptBaudRate(comPort : word; baudRate : real; brclock : real); var divisor : word; begin divisor := word(trunc((brclock/baudRate)/16 + 0.5)); SetBRDivisor(comPort,divisor); end; procedure SetBaudRate(comPort : word; baudRate : real); begin SetOptBaudRate(comport,baudrate,StdBRClock); end; procedure EnableIntDriver(comPort : word); var portSave : word; begin portSave := port[comPort + ModemCont]; port[comPort + ModemCont] := portSave or Out2Bit; end; procedure DisableIntDriver(comPort : word); var portSave : word; begin portSave := port[comPort + ModemCont]; port[comPort + ModemCont] := portSave and ($FF xor Out2Bit); end; procedure ForceDTRLow(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := dtemp and ($FF xor DTRBit); end; procedure ForceDTRHigh(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := (dtemp or DTRBit); end; procedure ForceRTSLow(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := dtemp and ($FF xor RTSBit); end; procedure ForceRTSHigh(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := (dtemp or RTSBit); end; procedure ForceOut1Low(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := dtemp and ($FF xor Out1Bit); end; procedure ForceOut1High(comPort : word); var dtemp : byte; begin dtemp := port[comPort + ModemCont]; port[comPort + ModemCont] := (dtemp or Out1Bit); end; procedure EnableFIFOs(comport : word); begin port[comPort + FIFOCont] := (FIFOEnable or RecvTrig8); end; procedure DisableFIFOs(comport : word); begin port[comPort + FIFOCont] := 0; end; { These 6I23 routines assume that the card jumpering is set } { for software enabled RS485 drive with power-up off drive enable polarity} procedure DriveOn6I23(comPort: word); begin ForceOut1Low(comPort); end; procedure DriveOff6I23(comPort: word); begin ForceOut1High(comPort); end; { These 4I23 routines assume that the card jumpering is set } { for software enabled RS485 drive with power-up off drive enable polarity} procedure DriveOn4I23(comPort: word); begin ForceRTSHigh(comPort); end; procedure DriveOff4I23(comPort: word); begin ForceRTSLow(comPort); end; procedure PolledGetComChar(comPort : word; var cchar : char); begin while (port[comPort + LineStat] and DataReady) = 0 do {wait for rec char} ; cchar := char(port[comPort + RecReg]); end; function PolledGetComCharWithTimeout(comPort : word; var cchar : char; ctimeout : longint) : longint; begin while ((port[comPort + LineStat] and DataReady) = 0) and (ctimeout <> 0) do ctimeout := ctimeout -1; {wait for rec char} ; if ctimeout <> 0 then cchar := char(port[comPort + RecReg]); PolledGetComCharWithTimeout := ctimeout; end; procedure TossChars(comPort : word;n: byte); var count : byte; thechar : char; begin {old for count := 1 to n do thechar := port[comPort+RecReg]; } for count := 1 to n do PolledGetComCharWithTimeout(comPort,thechar,100); { for 4i63 } end; procedure DefaultComInit(comPort : word); begin DisableIntDriver(comPort); port[comPort + LineCont] := DefaultLCR; port[comPort + IntEn] := DefaultIER; port[comPort + ModemCont] := DefaultMCR; TossChars(comPort,18); { make real sure there are no pending chars } end; function SKeyPressed : boolean; var flag : boolean; begin asm mov ah,01 int $16 mov flag,false jz @nokey mov flag,true @nokey: end; SKeyPressed := flag; end; function CTSHighQ(comport : word): boolean; begin if port[comport +ModemStat] and CTSBit = 0 then CTSHighQ := false else CTSHighQ := true; end; procedure WaitForCTSHigh(comport : word); begin while not CTSHighQ(comport) do; end; procedure WaitForCTSHighWithTimeout(comport : word;var timeout : longint); var loops : longint; begin loops := 0; while not CTSHighQ(comport) and (loops < timeout) do loops := loops +1; timeout := loops; end; function PolledGetComCharWithKeyAbort(comPort : word; var cchar : char): boolean; begin while ((port[comPort + LineStat] and DataReady) = 0) and (not SKeyPressed) do ; { wait for rec char or key pressed } if not SKeyPressed then begin cchar := char(port[comPort + RecReg]); PolledGetComCharWithKeyAbort := false; end else begin PolledGetComCharWithKeyAbort := true; end; end; procedure PolledPutComChar(comPort : word; cchar : char); begin while (port[comPort + LineStat] and THRE) = 0 do {wait for xmit char} ; port[comPort + TrHReg] := byte(cchar); end; procedure WaitForXmitDone(comport : word); begin while (port[comPort + LineStat] and (TSRE or THRE)) = 0 do {wait for all xmit chars to be gone }; end; procedure Polled485PutComChar(comPort : word; cchar : char); begin ForceRTSHigh(comPort); while (port[comPort + LineStat] and THRE) = 0 do {wait for xmit char} ; port[comPort + TrHReg] := byte(cchar); WaitForXmitDone(comPort); ForceRTSLow(comPort); end; procedure PolledPutComCharWithHandshake(comPort : word; cchar : char); begin while ((port[comPort + ModemStat] and CTSBit) = 0) and (not SKeyPressed) do ; { wait for cts or key pressed } if not SKeyPressed then begin port[comPort + TrHReg] := byte(cchar); {write the char} while (port[comPort + LineStat] and TSRE) = 0 do {wait for xmit char} ; end; end; procedure PolledPutComCharWithCTSHandshakeAndTimeout(comPort : word; cchar : char; timeout : longint); begin WaitForCTSHighWithTimeout(comPort,timeout); begin port[comPort + TrHReg] := byte(cchar); {write the char} while (port[comPort + LineStat] and TSRE) = 0 do {wait for xmit char} ; end; end; procedure PutOneComChar(comPort : word; cchar : char); begin port[comPort + TrHReg] := byte(cchar); WaitForXmitDone(comport); end; procedure SetUpForRecInt(comPort : word); var loop,temp : byte; begin port[comPort + IntEn] := DAvIntEn or RxErrIntEn; for loop := 0 to 15 do begin temp := port[comPort + RecReg]; temp := port[comPort + LineStat]; temp := port[comPort + IntId]; temp := port[comPort + ModemStat]; end; EnableIntDriver(comPort); end; procedure SetUpForXmitInt(comPort : word); var loop,temp : byte; begin EnableIntDriver(comPort); for loop := 0 to 15 do port[comPort + IntEn] := TXRdyIntEn; begin temp := port[comPort + IntId]; temp := port[comPort + RecReg]; temp := port[comPort + LineStat]; temp := port[comPort + ModemStat]; end; end; procedure ForceIRQLow(comPort : word); begin EnableIntDriver(comPort); port[comPort + IntEn] := 0; end; {**** Software FIFO stuff for int driven examples ****} procedure InitFIFO(fifo : FifteenCharFIFO); begin with fifo do begin PutPointer := 0; GetPointer := 0; end; end; function FIFOHasData(fifo : FifteenCharFIFO): boolean; begin FIFOHasData := false; with fifo do begin if GetPointer <> PutPointer then FIFOHasData := true; end; end; function FIFOIsFull(fifo : FifteenCharFIFO): boolean; begin FIFOIsFull := false; with fifo do begin { if hypothetical put put would smash get data then fifo is full! } { + 2 is to avoid empty=full case (and why its only 15 chars long } if ((PutPointer + 2) and $0F) = GetPointer then FIFOIsFull := true; end; end; function FIFOGet(fifo : FifteenCharFIFO) : word; begin if FIFOHasData(fifo) then begin with fifo do begin FIFOGet := DataBuffer[GetPointer] or (ErrorBuffer[GetPointer] * 256); GetPointer := (GetPointer +1) and $0F; end; end; end; procedure FIFOPut(fifo : FifteenCharFIFO;data :word); begin if not FIFOIsFull(fifo) then begin with fifo do begin DataBuffer[PutPointer] := byte(data); ErrorBuffer[PutPointer] := byte(data shr 8); PutPointer := (PutPointer +1) and $0F; end; end; end; function GetRecIntChar(comPort : word;var achar : char) : boolean; var temp : byte; begin temp := port[comPort+IntId]; if temp and $7 = 4 then { valid rec char or FIFO timeout on '550 } begin achar := char(port[comPort + RecReg]); GetRecIntChar := true; end else begin temp := port[comPort +LineStat]; GetRecIntChar := false; end; port[comPort + IntEn] := 0; { make sure we generate a new IRQ edge } port[comPort + IntEn] := DAvIntEn or RxErrIntEn; { by disabling then re-enabling the IRQ } end; function COMPortThere(comport : word) : boolean; var tdata : byte; bdata : word; olddivisor :word; begin GetBRDivisor(comport,olddivisor); COMPortThere := true; { used to start at 0 but Exar UARTs wont allow 0 in divisor reg } for tdata := 1 to 100 do begin SetBRDivisor(comport,tdata*631); GetBRDivisor(comport,bdata); if bdata <> (tdata * 631) then ComPortThere := false; end; SetBRDivisor(comport,olddivisor); end; function CarefulCOMPortThere(comport : word) : boolean; var tdata : byte; bdata : word; olddivisor :word; oldscratch : byte; begin CarefulComPortThere := false; oldscratch := port[comport + $07]; port[comport+$07] := $55; tdata := port[comport +$07]; port[comport+$07] := (tdata xor $FF); if tdata = $AA then begin GetBRDivisor(comport,olddivisor); CarefulCOMPortThere := true; { used to start at 0 but Exar UARTs wont allow 0 in divisor reg } for tdata := 1 to 100 do begin SetBRDivisor(comport,tdata*631); GetBRDivisor(comport,bdata); if bdata <> (tdata * 631) then CarefulComPortThere := false; end; SetBRDivisor(comport,olddivisor); end; port[comport + $0F] := oldscratch end; function COMPortType(comport : word) : PortType; var tdata : byte; thetype : PortType; begin thetype := CP16450; for tdata := 0 to 255 do begin port[comport+Scratch] := tdata; port[comport+ModemStat] := $FF xor tdata; if port[comport + Scratch] <> tdata then thetype := CP8250; end; if thetype <> CP8250 then begin port[comport+FIFOCont] := 0; if (port[comport+FIFOCont] and FIFOsOn) = 0 then begin port[comport+FIFOCont] := FIFOEnable; if (port[comport+FIFOCont] and FIFOsOn) <> 0 then thetype := CP16550; port[comport+FIFOCont] := 0; end; end; COMPortType := thetype; end; procedure PrintComPortType(comport : word); begin writeln; case ComPortType(comport) of CP8250 : writeln('COM port type is 8250'); CP16450 : writeln('COM port type is 16450'); CP16550 : writeln('COM port type is 16550'); end; end;