
Unit Modem;
{$O+,F+}

InterFace

uses dos;

const crlf:string[2] = #13+#10;

type
  pDriverInfo = ^DriverInfo;
  DriverInfo = record
      Size  : Word;
      Spec  : byte;
      Rev   : Byte;
      ID    : String;
      Ibuf  : Word;
      Iavl  : Word;
      OBuf  : Word;
      Oavl  : Word;
      Width : Byte;
      Height: Byte;
      Baud  : Byte;
    end;

Type
 pModemObj = ^ModemObj;
 ModemObj = object
 Function  Init(c:byte):boolean;
 Procedure Close;

 Function  Spec:byte;
 Function  Rev :byte;
 Function  ID  :String;
 Function  InputBuf : word;
 Function  OutputBuf: word;
 Function  DTEBaud: word;
 Function  OutBufUsed: word;
 Function  InBufUsed: word;

 Procedure SetParams(baud_:word; DataBits:byte; Parity:char; StopBit:byte);
 Procedure SetDtr(state:boolean);
 Procedure SetFlow(state:boolean);
 Function  CD:boolean;
 Procedure KillOut;
 Procedure KillIn;
 Procedure Flush;
 Function  Available:boolean;
 Function  OkToSend:boolean;
 Function  Empty:boolean;
 Procedure WriteChar(c:char);
 Procedure Write(s:string);
 Procedure Writeln(s:string);
 Function  Readkey:char;
 Function  HangUp:boolean;
 Function  ATCommand(command:string):boolean;
 Function  PeekAhead:char;
 Procedure Snarf(n:word);
 Procedure ReadBlock(var s:string;reqnum,maxnum:byte);

 Private
   ComPortVal: byte;
   DriverInfo: pDriverInfo;
   Procedure SetComPort(c:byte);
   Function ComPort:byte;
   Procedure  GetInfo;
 end;

IMPLEMENTATION

uses Crt; { needed for Delay() }

Procedure ModemObj.GetInfo;
  type
   DvrInfo = Record
      Size  : Word;
      Spec  : byte;
      Rev   : Byte;
      ID    : Pointer;
      Ibuf  : Word;
      Iavl  : Word;
      OBuf  : Word;
      Oavl  : Word;
      Width : Byte;
      Height: Byte;
      Baud  : Byte;
    End;

  var regs:registers;
      di: dvrinfo;
      ts: string;
  Begin
  fillchar(regs,sizeof(regs),0);

    Regs.Cx := Sizeof(di);
    Regs.Es := Seg(di);
    Regs.Di := Ofs(di);
    Regs.Dx := comport-1;
    Regs.ah := $1B;
    Intr($14,Regs);

  DriverInfo^.Size := di.size;
  DriverInfo^.spec := di.spec;
  DriverInfo^.rev  := di.rev;

  DriverInfo^.ibuf := di.ibuf;
  DriverInfo^.iavl := di.iavl;
  DriverInfo^.obuf := di.obuf;
  DriverInfo^.oavl := di.oavl;
  DriverInfo^.width := di.width;
  DriverInfo^.height := di.height;
  DriverInfo^.baud := di.baud;

  move ( di.id^, mem[seg(ts):ofs(ts)+1], sizeof(ts)-1);

  ts[0]:=#255;

  ts[0]:=char(pos(#0,ts)-1);

  DriverInfo^.id:=ts;

  End;

Function  ModemObj.Spec:byte;
 begin
 Spec := DriverInfo^.Spec;
 end;

Function  ModemObj.Rev :byte;
 begin
 Rev:=DriverInfo^.Rev;
 end;

Function  ModemObj.ID  :String;
 begin
 ID:=DriverInfo^.id;
 end;

Function  ModemObj.InputBuf : word;
 begin
 InputBuf := DriverInfo^.IBuf;
 end;

Function  ModemObj.OutputBuf: word;
 begin
 OutputBuf := DriverInfo^.OBuf;
 end;

{

                010 =   300 baud
                011 =   600  ''
                100 =  1200  ''
                101 =  2400  ''
                110 =  4800  ''
                111 =  9600  ''
                000 = 19200  '' (Replaces old 110 baud mask)
                001 = 38400  '' (Replaces old 150 baud mask)
}

Function  ModemObj.DTEBaud: word;
 begin
 case driverinfo^.baud of
   0: dtebaud := 19200;
   1: dtebaud := 38400;
   2: dtebaud := 300;
   3: dtebaud := 600;
   4: dtebaud := 1200;
   5: dtebaud := 2400;
   6: dtebaud := 4800;
   7: dtebaud := 9600;
   else dtebaud := 0;
   end;
 end;

Function  ModemObj.OutBufUsed:word;
 begin
 GetInfo;
 OutBufUsed := DriverInfo^.OAvl;
 end;

Function  ModemObj.InBufUsed:word;
 begin
 GetInfo;
 InBufUsed := DriverInfo^.IAvl;
 end;

function ModemObj.PeekAhead:char;
 var regs:registers;
 begin
 fillchar(regs,sizeof(regs),0);
 with regs do
   begin
   ah:=$0C;
   dx:=comport-1;
   end;
 Intr($14, regs);

 if regs.ax=$ffff then PeekAhead:=#0 else
   PeekAhead:=char(regs.al);

 end;


Procedure ModemObj.SetComPort(c:byte);
 begin
 ComPortVal:=c;
 end;

Function ModemObj.ComPort:byte;
 begin;
 ComPort:=ComPortVal;
 end;

Function ModemObj.Init(c: Byte):boolean;
var regs:registers;
Begin
 ModemObj.SetComPort(c);

 FillChar(regs,sizeof(regs),0);
 With Regs Do
 Begin
    AH := 4;
    DX := (ComPort-1);
 end;
 Intr($14, Regs);
 if (regs.AX=$1954) then
  begin
  Init:=True;
  New(DriverInfo);
  GetInfo;
  end
 else Init:=False;
end;

Procedure ModemObj.Close;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);

  {a setdtr(false) used to be here}

  With Regs Do
  Begin
    AH := 5;
    DX := (ComPort-1);
    Intr($14, Regs);
  end;
  dispose(Driverinfo);
end;

{
|   AH = 18h    Read block (transfer from FOSSIL to user buffer)

|           Parameters:
|               Entry:  CX = Maximum number of characters to transfer
|                       DX = Port number
|                       ES = Segment of user buffer
|                       DI = Offset into ES of user buffer
|               Exit:   AX = Number of characters actually transferred

|   A "no-wait"  block read of 0 to FFFFh characters from the FOSSIL inbound
|   ring buffer to the calling routine's buffer. ES:DI are left unchanged by
|   the call; the count of bytes actually transferred will be returned in AX.
}

Procedure ModemObj.ReadBlock(var s:string;reqnum,maxnum:byte);
 var regs:registers;
     t:string;
 begin
 with regs do
   begin
   if maxnum<=255 then cx := maxnum else cx := 255;
   dx := comport-1;
   es := seg(s);
   di := ofs(s) +1;
   end;
 Intr($14, regs);

 s[0] := chr(regs.ax);

 if regs.ax<reqnum then
   begin
   t:='';
   ReadBlock(t,reqnum-regs.ax,maxnum-regs.ax);
   s:= concat (s,t);
   end;

 end;

Procedure ModemObj.SetParams (Baud_: Word; DataBits: Byte; Parity: Char;
                     StopBit: Byte);
Var
 Code: Integer;
 regs:registers;

Begin
  Code:=0;
  FillChar(regs,sizeof(regs),0);
    Case Baud_ of
      0 : Exit;
  19200 : code:=code+000+00+00;
  38400 : code:=code+000+00+32;
    300 : code:=code+000+64+00;
    600 : code:=code+000+64+32;
    1200: code:=code+128+00+00;
    2400: code:=code+128+00+32;
    4800: code:=code+128+64+00;
    9600: code:=code+128+64+32;
  end;

  Case DataBits of
    5: code:=code+0+0;
    6: code:=code+0+1;
    7: code:=code+2+0;
    8: code:=code+2+1;
  end;

  Case Parity of
    'N','n': code:=code+00+0;
    'O','o': code:=code+00+8;
    'E','e': code:=code+16+8;
  end;

  Case StopBit of
    1: code := code + 0;
    2: code := code + 4;
  end;

  With Regs do
  begin
    AH := 0;
    AL := Code;
    DX := (ComPort-1);
    Intr($14, Regs);
  end;
end;

Procedure ModemObj.SetDtr   (State: Boolean);
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  begin
    AH := 6;
    DX := (ComPort-1);
    Case State of
    True : AL := 1;
    False: AL := 0;
    end;
    Intr($14, Regs);
  end;
end;


Function  ModemObj.CD     : Boolean;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 3;
    DX := (ComPort-1);
    Intr($14, Regs);
    CD := ((AL AND 128) = 128);
  end;
end;


Procedure ModemObj.SetFlow  (State: Boolean);
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
    With Regs do
    Begin
    AH := 15;
    DX := ComPort-1;
    Case State of
      TRUE:  AL := 255;
      FALSE: AL := 0;
    end;
    Intr($14, Regs);
  end;
end;

Procedure ModemObj.KillOut;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
    With Regs do
    begin
    AH := 9;
    DX := ComPort-1;
    Intr($14, Regs);
  end;
end;

Procedure ModemObj.Snarf(n:word);
 var c:char;
     i:word;
 begin
 for i:=1 to n do c:=readkey;
 end;


Procedure ModemObj.KillIn  ;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  begin
    AH := 10;
    DX := ComPort-1;
    Intr($14, Regs);
  end;
end;

Procedure ModemObj.Flush ;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 8;
    DX := ComPort-1;
    Intr($14, Regs);
  end;
end;

Function  ModemObj.Available: Boolean;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 3;
    DX := ComPort-1;
    Intr($14, Regs);
    Available:= ((AH AND 1) = 1);
  end;
end;

Function  ModemObj.OkToSend : Boolean;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 3;
    DX := ComPort-1;
    Intr($14, Regs);
    OkToSend := ((AH AND 32) = 32);
  end;
end;


Function  ModemObj.Empty : Boolean;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 3;
    DX := ComPort-1;
    Intr($14, Regs);
    Empty := ((AH AND 64) = 64);
  end;
end;

Procedure ModemObj.WriteChar (C: Char);
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 1;
    DX := ComPort-1;
    AL := ORD (C);
    Intr($14, Regs);
  end;
end;

{
|   AH = 19h    Write block (transfer from user buffer to FOSSIL)

|           Parameters:
|               Entry:  CX = Maximum number of characters to transfer
|                       DX = Port number
|                       ES = Segment of user buffer
|                       DI = Offset into ES of user buffer
|               Exit:   AX = Number of characters actually transferred


|   A  "no-wait"  block  move of 0  to FFFFh  characters  from  the  calling
|   program's  buffer into  the  FOSSIL outbound ring buffer. ES:DI are left
|   unchanged by the call;  the count of bytes actually transferred  will be
|   returned in AX.
}


Procedure ModemObj.Write ( s: String);
Var
  regs:registers;
begin
 FillChar(regs,sizeof(regs),0);

 with regs do
  begin
  ah := $19;
  cx := ord(s[0]);
  dx := comport-1;
  es := seg(s);
  di := ofs(s)+1;
  Intr($14, regs);
  end;

 if not(regs.ax=ord(s[0])) then
   begin
   Flush;
   Write(copy(s,regs.ax+1,ord(s[0])));
   end;
end;


Procedure ModemObj.Writeln (s: String);
Var
 regs:registers;
begin
   Write(s+crlf);
end;

Function ModemObj.Readkey : Char;
var regs:registers;
Begin
  FillChar(regs,sizeof(regs),0);
  With Regs do
  Begin
    AH := 2;
    DX := ComPort-1;
    Intr($14, Regs);
    Readkey := Chr(AL);
  end;
end;


Function ModemObj.Hangup  : Boolean;
var
  Tcount : Integer;
  var regs:registers;
begin
  ModemObj.SetDtr(FALSE);
  delay (600);
  ModemObj.SetDtr(TRUE);
  if ModemObj.CD=true then begin
    Tcount:=1;
      repeat
        ModemObj.Write ('+++');
        delay (3000);
        ModemObj.Writeln ('ATH0');
        delay(3000);
        if ModemObj.CD=false then tcount:=3;
        Tcount:=Tcount+1;
      until Tcount=4;
  end;

 hangup:=not(ModemObj.cd=true)
end;

Function ModemObj.AtCommand (Command: String) : Boolean;
Var
  Ch     :   Char;
  Result :   String[50];
  I,Z    :   Integer;
Begin
  AtCommand:=FALSE;
  Result:='';
  For Z:=1 to 3 do begin
    Write (Char(13));
    Writeln (Command);
    Delay(500);
    Repeat
      If Available then Begin
        Ch:=Readkey;
        Result:=Result+Ch;
      end;
    Until Available=FALSE;
    For I:=1 to Length(Result)-2 do Begin
      If copy(result,i,2)='OK' then Begin
        AtCommand:=TRUE;
       Exit;
      end;
    end;
  end;
End;


END.

