返回
{***************************************************************}
{***               FVision Unit Version1.0                   ***}
{***                    蓝蚂蚁工作室                         ***}
{***************************************************************}
{***                  串行通讯支持单元                       ***}
{***************************************************************}
{$O+,F+,X+,I-}
Unit FCommu;
Interface
Uses
  Dos,Crt;

Const
  {Port Code}
  sioCOM1         = $0000;
  sioCOM2         = $0001;
  sioCOM3         = $0002;
  sioCOM4         = $0003;

  {Register Delta Addr}
  sioDATA         = 0;   { data register                    }
  sioBAUDL        = 0;   { baud rate divisor (LSB)          }
  sioIER          = 1;   { Interrupt Enable Register        }
  sioBAUDH        = 1;   { baud rate divisor (MSB)          }
  sioIIR          = 2;   { Interrupt Identification Register}
  sioLCR          = 3;   { Line Control Register            }
  sioMCR          = 4;   { Modem Control Register           }
  sioLSR          = 5;   { Line Status Register             }
  sioMSR          = 6;   { Modem Status Register            }

  {Modem Status Code}
  sioDCD          = $80;
  sioRI           = $40;
  sioDSR          = $20;
  sioCTS          = $10;
  sioDeltaDCD     = $08;
  sioDeltaRI      = $04;
  sioDeltaDSR     = $02;
  sioDeltaCTS     = $01;

  {Parity Code}
  sioNoParity     = 0;
  sioOddParity    = 1;
  sioEvenParity   = 3;
  sioMarkParity   = 5;
  sioSpaceParity  = 7;

  {Stop Bits Code}
  sioOneStopBit   = 0;
  sioTwoStopBits  = 1;

  {Code Bits Code}
  sioCodeLength5  = 0;
  sioCodeLength6  = 1;
  sioCodeLength7  = 2;
  sioCodeLength8  = 3;

  {Line Status Code}
  sioTransEmpty   = $20;
  sioBreakDetect  = $10;
  sioFramingError = $08;
  sioParityError  = $04;
  sioOverrunError = $02;
  sioDataReady    = $01;


Const
  dfBufLen:Word       = 10000;
  dfSendBufLen:Word   = 1000;
  dfBaud:Longint      = 1200;
  dfDataBits:Integer  = 7;
  dfStopBits:Integer  = 1;
  dfParity:Char       = 'N';
  dfComPort:Integer   = 1;
  dfExecOldInt:Boolean= True;
  ComIndex:Integer    = 1;
  SioBasePort:array[1..4] of Integer = ($3F8,$2F8,$2E8,$2E8);

Var
  AsyncVector:Pointer;
  CharsInBuf,CircIn,CircOut:Word;
  SendCharsInBuf,SendIn,SendOut:Word;

Procedure ClearReceiveBuffer;
Procedure ClearSendBuffer;
Procedure InitSerialPort(BPS:Longint;StopBits,DataBits:Integer;Parity:Char);
Procedure DoneSerialPort;
Procedure EnableSerialInt;
Procedure DisableSerialInt;
Function  InitBuffer:Boolean;
Procedure DoneBuffer;
Function  GetCharInBuf:Byte;
Function  CarrierDetected:Boolean;
Function  SendCharToBuf(Ch:Byte):Boolean;
Procedure SendChar(B:Byte);
Procedure SendString(S:string);
Procedure SendByte;
Function  GetChar:Byte;
Function  GetModemStatus:Byte;
Function  GetComAddr(Com:Integer):Word;

Implementation
Type
  BufferType=array[1..1] of Byte;

Const
  IerSR    = $03;
  IntCtrl  = $21;

Const
  ComIntNum:array[1..4] of Integer=($0C,$0B,$0C,$0B);
  ComInitFlag:Boolean=False;
  ComPortIntFlag:Boolean=False;

Var
  R:Registers;
  ReceiveBuffer,SendBuffer:^BufferType;
  OldIntCtrl:Byte;
  OldDLL,OldDLM:Byte;
  OldIER:Byte;
  OldMCR:Byte;
  OldLCR:Byte;

Procedure  CallOldInt(Sub:Pointer);
begin
  inline($9C/
         $FF/$5E/$06);
end;

Procedure ClearReceiveBuffer;
begin
  CircIn:=1;
  CircOut:=1;
  CharsInBuf:=0;
end;

Procedure ClearSendBuffer;
begin
  SendIn:=1;
  SendOut:=1;
  SendCharsInBuf:=0;
end;

Function SendCharToBuf(Ch:Byte):Boolean;
begin
  SendCharToBuf:=False;
  if SendCharsInBuf<dfSendBufLen then
  begin
    SendCharToBuf:=True;
    SendBuffer^[SendOut]:=Ch;
    Inc(SendCharsInBuf);
    if SendOut=dfSendBufLen then
      SendOut:=1
    else
      Inc(SendOut);
  end;
end;

Function Wait: Boolean;
var
  Ch:Char;
begin
  Wait:=False;
  if KeyPressed then
  begin
    Ch:=ReadKey;
    if Ch=#27 then Wait:=True
    else if Ch=#0 then Ch:=ReadKey;
  end;
end;

Procedure AsyncInt;Interrupt;
var
  TempIIR:Byte;
begin
  TempIIR:=Port[SioBasePort[ComIndex]+sioIIR];
  if (TempIIR and $07)=$04 then
  begin
   if (CharsInBuf<dfBufLen) then
   begin
     ReceiveBuffer^[CircIn]:=Port[SioBasePort[ComIndex]];
     if (CircIn<dfBufLen) then
       inc(CircIn)
     else
       CircIn:=1;
     inc(CharsInBuf);
   end
  end else
  if (TempIIR and $07)=$02 then
  begin
   if (SendCharsInBuf>0) then
   begin
     Port[SioBasePort[ComIndex]]:=SendBuffer^[SendIn];
     if (SendIn<dfSendBufLen) then
       inc(SendIn)
     else
       SendIn:=1;
     dec(SendCharsInBuf);
   end;
  end;
  if dfExecOldInt then
    CallOldInt(AsyncVector);
  Port[$20]:=$20;
end;

Procedure InitSerialPort(BPS:Longint;StopBits,DataBits:Integer;Parity:char);
var
  Temp:Byte;
  BPSDiv:Word;
begin
  if ComInitFlag then Exit;
  ComIndex:=dfComPort;
  OldLCR:=Port[SioBasePort[ComIndex]+sioLCR];
  Port[SioBasePort[ComIndex]+sioLCR]:=Port[SioBasePort[ComIndex]+sioLCR] or $80;
  OldDLM:=Port[SioBasePort[ComIndex]+sioBAUDH];
  OldDLL:=Port[SioBasePort[ComIndex]+sioBAUDL];
  BPSDiv:=Word(115200 div BPS);
  Port[SioBasePort[ComIndex]+sioBAUDH]:=Hi(BPSDiv);
  Port[SioBasePort[ComIndex]+sioBAUDL]:=Lo(BPSDiv);
  Temp:=((StopBits-1) shl 2) + (DataBits-5);
  case Parity of
  'E':Inc(Temp,24);
  'O':Inc(Temp,8);
  end;
  Port[SioBasePort[ComIndex]+sioLCR]:=Temp;
  ComInitFlag:=True;
end;

Procedure DoneSerialPort;
begin
  if not ComInitFlag then Exit;
  Port[SioBasePort[ComIndex]+sioLCR]:=Port[SioBasePort[ComIndex]+sioLCR] or $80;
  Port[SioBasePort[ComIndex]+sioBAUDH]:=OldDLM;
  Port[SioBasePort[ComIndex]+sioBAUDL]:=OldDLL;
  Port[SioBasePort[ComIndex]+sioLCR]:=OldLCR;
  ComInitFlag:=False;
end;

Function InitBuffer:Boolean;
begin
  GetMem(ReceiveBuffer,dfBufLen);
  GetMem(SendBuffer,dfSendBufLen);
  InitBuffer:=(ReceiveBuffer<>nil) and (SendBuffer<>nil);
end;

Procedure DoneBuffer;
begin
  if ReceiveBuffer<>nil then
    FreeMem(ReceiveBuffer,dfBufLen);
  if SendBuffer<>nil then
    FreeMem(SendBuffer,dfSendBufLen);
  ReceiveBuffer:=nil;
  SendBuffer:=nil;
end;

Procedure EnableSerialInt;
var
  B:Byte;
begin
  if ComPortIntFlag then Exit;
  InitBuffer;
  GetIntVec(ComIntNum[ComIndex],AsyncVector);
  SetIntVec(ComIntNum[ComIndex],@AsyncInt);
  OldIntCtrl:=Port[IntCtrl];
  OldIER:=Port[SioBasePort[ComIndex]+sioIER];
  OldMCR:=Port[SioBasePort[ComIndex]+sioMCR];
  B:=OldIntCtrl;
  if Odd(ComIndex) then
    B:=B and $0EF
  else
    B:=B and $0F7;
  Port[IntCtrl]:=B;
  Port[SioBasePort[ComIndex]+sioLCR]:=Port[SioBasePort[ComIndex]+sioLCR] and $7F;
  Port[SioBasePort[ComIndex]+sioIER]:=IerSR;
  Port[SioBasePort[ComIndex]+sioMCR]:=$0B;
  B:=Port[SioBasePort[ComIndex]+sioIIR];
  ComPortIntFlag:=True;
end;

Procedure DisableSerialInt;
var
  B:Byte;
begin
  if not ComPortIntFlag then Exit;
  Port[IntCtrl]:=OldIntCtrl;
  Port[SioBasePort[ComIndex]+sioLCR]:=Port[SioBasePort[ComIndex]+sioLCR] and $7F;
  Port[SioBasePort[ComIndex]+sioIER]:=OldIER;
  Port[SioBasePort[ComIndex]+sioMCR]:=OldMCR;
  SetIntVec(ComIntNum[ComIndex],AsyncVector);
  B:=Port[SioBasePort[ComIndex]+sioIIR];
  DoneBuffer;
  ComPortIntFlag:=False;
end;

Function GetCharInBuf:Byte;
begin
  if CharsInBuf>0 then
  begin
    GetCharInBuf:=ReceiveBuffer^[CircOut];
    if CircOut<dfBufLen then
      inc(CircOut)
    else
      CircOut:=1;
    dec(CharsInBuf);
  end;
end;

Function CarrierDetected:Boolean;
var
  Ch:Char;
  Timer:Integer;
begin
  CarrierDetected:=False;
  Timer:=40;
  while (Port[SioBasePort[ComIndex]+sioMSR] and $80)<>$80 do
  begin
    if Wait then Exit;
    if (CharsInBuf>0) then
    begin
      Ch:=Char(GetCharInBuf);
      Write(Ch);
    end;
    if Timer=0 then
      Exit
    else
    begin
      Dec(Timer);
      Delay(1000);
    end;
  end;
  CarrierDetected:=True;
end;

Procedure SendChar(B:Byte);
begin
  while ((Port[SioBasePort[ComIndex]+sioLSR] and $60)<>$60) do
  begin
    if Wait then Exit;
  end;
  Port[SioBasePort[ComIndex]]:=B;
end;

Procedure SendString(s:string);
var
  i:integer;
begin
  for i:=1 to length(s) do
    SendChar(ord(s[i]));
end;

Procedure SendByte;
begin
  if (SendCharsInBuf>0) then
  begin
    while ((Port[SioBasePort[ComIndex]+sioLSR] and $60)<>$60) do
    begin
      if wait then exit;
    end;
    Port[SioBasePort[ComIndex]]:=SendBuffer^[SendIn];
    if (SendIn<dfSendBufLen) then
      inc(SendIn)
    else
      SendIn:=1;
    dec(SendCharsInBuf);
  end;
end;

Function GetChar:Byte;
begin
  if ((Port[SioBasePort[ComIndex]+sioLSR] and $01)=$01) then
    GetChar:=Port[SioBasePort[ComIndex]]
  else
    GetChar:=0;
end;

Function GetModemStatus:Byte;
begin
  GetModemStatus:=Port[SioBasePort[ComIndex]+sioMSR];
end;

Function GetComAddr(Com:Integer):Word;
begin
  GetComAddr:=MemW[$0000:($0400+(Com-1) shl 1)];
end;

end.