home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1997 October / PCO1097.ISO / FilesBBS / WIN3X / MSTARTER.ARJ / COMMUNIT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-02-14  |  15.0 KB  |  423 lines

  1. {----------------------------------------------------------------}
  2. { COMMUNIT   interruptgesteuerte V24-Kommunikation               }
  3. {            (c) 1994 Peter Zwosta                               }
  4. {----------------------------------------------------------------}
  5. Unit CommUnit;
  6. {$S-}
  7.  
  8. INTERFACE { -----------------------------------------------------}
  9.  
  10. uses Crt, Dos, CommCons, CommHelp;
  11.  
  12. const
  13.  
  14.   isComIntSet         : boolean = false;  { Handler gesetzt ?    }
  15.   Test_Modem_and_Port : boolean = true;   { vgl. InitComPort     }
  16.   FIFO_Automatic      : boolean = true;   { autmat. FIFO-Support }
  17.  
  18.   { Resultwerte für InitComPort  (Test_Modem_and_Port = true     }
  19.   InitOK    = $0000;
  20.   NoModem   = $0001;
  21.   NoPort    = $0002;
  22.   InitError = $0004;
  23.  
  24.  
  25. { Serielle Routinen }
  26. Function InitComPort(aPort     : word;
  27.                      Baud      : BaudType;
  28.                      DataBits  : byte;
  29.                      StopBits  : byte;
  30.                      Parity    : byte;
  31.                      HandShake : word) : word; { vgl. Resultwerte  }
  32. Procedure CloseComPort;
  33. Function  V24ReadCh : char;
  34. Procedure V24TimedReadCh(var ch : char; var isTimeOut : boolean);
  35. Function  V24WriteCh(ch : char) : boolean;  { liefert TRUE,         }
  36.                                            { wenn erfolgreich      }
  37. Function V24WriteStr(s : string) : boolean;
  38. Function V24WriteCommand(s : string) : boolean;
  39.  
  40. Procedure Set_DTR_RTS;
  41. Procedure ClearBuffer;
  42.  
  43. { Erlaubt und verhindert externe Hardw.Interrupts }
  44. Procedure EnableInts;
  45. Procedure DisableInts;
  46.  
  47. { Installieren und Entfernen des Interrupt-Vectors }
  48. Procedure SetInt;
  49. Procedure ResetInt;
  50.  
  51. IMPLEMENTATION { ------------------------------------------------}
  52.  
  53. const
  54.  
  55.   BufSize    = 9192;  { Größe des Puffers  (InputBuffer)         }
  56.  
  57.   WaitCh    =  '~';
  58.   Attention =  '^';
  59.   Enter     =  'M';
  60.  
  61. var HandshakeType : word;
  62.     OldIntVector  : pointer;
  63.     OldExitProc   : pointer;
  64.     ComPort       : word;
  65.     InputBuffer   : array[1..BufSize] of char;
  66.     StartInBuf,
  67.     EndInBuf          : integer;
  68.  
  69. { ------------------------------------------------------------- }
  70. { NOMODEMFOUND true, wenn nach dem Initialisieren alle Bits des }
  71. {              Modemstatusregisters 0 sind.                     }
  72. { ------------------------------------------------------------- }
  73. Function NoModemFound : boolean;
  74. begin
  75.   NoModemFound := ((Port[ComBase[ComPort] + MSR] and $FF) = 0);
  76. end;
  77. { ------------------------------------------------------------- }
  78. { NOPORTFOUND true, wenn nach dem Initialisieren eins der       }
  79. {             Deltas > 0 ist.                                   }
  80. { ------------------------------------------------------------- }
  81. Function NoPortFound : boolean;
  82. begin
  83.   NoPortFound := ((Port[ComBase[ComPort] + MSR]  and $0F) > 0);
  84. end;
  85. { ------------------------------------------------------------- }
  86. { ENABLEINTS, DISABLEINTS                                       }
  87. { ------------------------------------------------------------- }
  88. Procedure EnableInts;
  89. begin
  90.   asm
  91.     STI
  92.   end;
  93. end;
  94. Procedure DisableInts;
  95. begin
  96.   asm
  97.     CLI
  98.   end;
  99. end;
  100. { ------------------------------------------------------------- }
  101. { INITCOMPORT Zum Installieren des ComPorts. Hierzu sind die    }
  102. {             Konstanten, die im Zusammenhang mit dem LSR       }
  103. {             definiert wurden zu verwenden.                    }
  104. { ------------------------------------------------------------- }
  105. Function InitComPort(aPort     : word;
  106.                      Baud      : BaudType;
  107.                      DataBits  : byte;
  108.                      StopBits  : byte;
  109.                      Parity    : byte;
  110.                      HandShake : word) : word;
  111.  
  112. var reg     : byte;
  113.     divisor : word;
  114.     Speed   : longint;
  115. begin
  116.   StartInBuf  := 1;               { Anfang der Chars in CHBuffer }
  117.   EndInBuf    := 1;   { Eins größer als die Position des letzten }
  118.                       { Chars in ChBuffer                        }
  119.   HandShakeType := HandShake;     { Merken, was für ein Handsh.  }
  120.  
  121.   ComPort       := aPort;
  122.   Speed         := BaudTab[ord(Baud)];
  123.  
  124.   { Geschwindigkeit setzen }
  125.   reg := Port[ComBase[aPort] + LCR] or DLAB;
  126.   Port[ComBase[aPort] + LCR] := reg;          { DLAB setzen      }
  127.   divisor := round(115200/Speed);
  128.   Port[ComBase[aPort] + DLL] := lo(divisor);
  129.   Port[ComBase[aPort] + DLH] := hi(divisor);
  130.   Port[ComBase[aPort] + LCR] := reg xor DLAB; { DLAB zurücksetzen}
  131.  
  132.   { Leitungssteuerregister setzen }
  133.   reg := DataBits or Stopbits or Parity;
  134.   Port[ComBase[aPort] + LCR] := reg;
  135.  
  136.   If FIFO_Automatic Then
  137.     begin
  138.       if Pos('16550', GetSIO(aPort)) > 0 Then
  139.         Switch16550(aPort, true, 8);
  140.     end;
  141.  
  142.   SetInt;          { PIC-Register setzen und Int-Vektor setzen }
  143.  
  144.   InitComport := InitOK;
  145.   If Test_Modem_and_Port Then
  146.     begin
  147.       If NoModemFound
  148.         Then InitComport := NoModem
  149.         Else If NoPortFound Then InitComport := NoPort;
  150.     end;
  151. end;
  152.  
  153. Procedure CloseComport;
  154. begin
  155.   ResetInt;
  156. end;
  157. { -------------------------------------------------------------- }
  158. { V24ReadCh liest ein Zeichen aus dem InputBuffer, wenn eins     }
  159. {          da ist.                                               }
  160. { -------------------------------------------------------------- }
  161. Function V24ReadCh : char;
  162. begin
  163.   V24ReadCh := #0;
  164.   If StartInBuf <> EndInBuf then
  165.     begin
  166.       DisableInts;
  167.       V24ReadCh  := InputBuffer[StartInBuf];
  168.       StartInBuf := (StartInBuf mod BufSize) + 1;
  169.       EnableInts;
  170.     end;
  171. end;
  172. { -------------------------------------------------------------- }
  173. { V24TimedReadCh liest ein Zeichen aus dem InputBuffer. Liefert  }
  174. {                isTimeOut = false zurück, wenn nach einer best. }
  175. {                Zeit kein Zeichen gelesen werden konnte.        }
  176. { -------------------------------------------------------------- }
  177. Procedure V24TimedReadCh(var ch : char; var isTimeOut : boolean);
  178. var TimeCount : word;
  179.  
  180. begin
  181.   TimeCount := 65535;
  182.   ch      := #0;
  183.   Repeat
  184.     DisableInts;
  185.     isTimeout := (StartInBuf = EndInBuf);
  186.     EnableInts;
  187.     dec(TimeCount);
  188.   until (TimeCount = 0) or (isTimeOut = false);
  189.   If (not isTimeOut) Then
  190.     begin
  191.       DisableInts;
  192.       ch := InputBuffer[StartInBuf];
  193.       StartInBuf := (StartInBuf mod BufSize) + 1;
  194.       EnableInts;
  195.     end;
  196. end;
  197. { -------------------------------------------------------------- }
  198. { V24WriteCh  schreibt ein Zeichen in das TH-Register            }
  199. { -------------------------------------------------------------- }
  200. Function V24WriteCh(ch : char) : boolean;
  201. var TimeCount : word;
  202. begin
  203.   V24WriteCh  := false;
  204.   TimeCount   := 65535;
  205.   { DTR und CTS setzen                                           }
  206.   If (HandshakeType and RTS_CTS) > 0 Then
  207.     begin
  208.        Set_DTR_RTS;                 { Wir sind fertig zum Senden }
  209.        { Auf Clear To send warten }
  210.        While ((Port[ComBase[ComPort] + MSR] and CTS) = 0) and
  211.               (TimeCount > 0) do dec(TimeCount);
  212.        if TimeCount > 0 Then TimeCount := 65535;
  213.     end;
  214.   { Warten bis der Transmitter (THR) leer ist                    }
  215.   While ((Port[ComBase[ComPort] + LSR] and THREmpty) = 0) and
  216.         (TimeCount > 0) do dec(TimeCount);
  217.   If TimeCount > 0 Then
  218.     begin
  219.      Port[ComBase[ComPort] + THR] := ord(ch);  { Zeichen ausgeben }
  220.      V24WriteCh := true;
  221.     end;
  222. end;
  223. { -------------------------------------------------------------- }
  224. { V24WriteStr  schreibt einen ganzen String ins TH-Register.     }
  225. { -------------------------------------------------------------- }
  226. Function V24WriteStr(s : string) : boolean;
  227. var i   : integer;
  228.     OK  : boolean;
  229. begin
  230.   OK := true;
  231.   For i := 1 to length(s) do
  232.     begin
  233.       OK := V24WriteCh(s[i]);
  234.       If not OK then Break;
  235.     end;
  236.   V24WriteStr := OK;
  237. end;
  238. { -------------------------------------------------------------- }
  239. { V24WriteCommand  schreibt einen String ins THR, ermöglicht     }
  240. {                  zusätzlich die Definition von Sonderzeichen,  }
  241. {                  die z.B. im Init-String für das Modem ver-    }
  242. {                  wendet werden können.                         }
  243. { -------------------------------------------------------------- }
  244. Function V24WriteCommand(s : string) : boolean;
  245. var OK : boolean;
  246.     i  : integer;
  247. begin
  248.   OK := true;
  249.   i := 0;
  250.   While i < length(s) do
  251.     begin
  252.       inc(i);
  253.       case s[i] of
  254.         WaitCh      : begin
  255.                         delay(500);
  256.                         continue;
  257.                       end;
  258.         Attention   :
  259.             begin
  260.               if (i+1) <= length(s) Then
  261.                 If s[i+1] = Enter Then
  262.                   begin
  263.                     inc(i);
  264.                     s[i] := #13;
  265.                   end;
  266.             end;
  267.       end;
  268.       OK := V24WriteCh(s[i]);
  269.       If not OK then Break;
  270.     end;
  271.   V24WriteCommand := OK;
  272. end;
  273.  
  274. { -------------------------------------------------------------- }
  275. { Set_DTR_RTS Modem-Control-Register mitteilen, daß das Prog.    }
  276. {             fertig zum Senden ist.                             }
  277. {             DTR : Data Terminal Ready                          }
  278. {             RTS : Request to send                              }
  279. { -------------------------------------------------------------- }
  280. Procedure Set_DTR_RTS;
  281. var reg : byte;
  282. begin
  283.   reg := Port[ComBase[ComPort] + MCR] or DTR or RTS;
  284.   Port[ComBase[ComPort] + MCR] := reg;
  285. end;
  286.  
  287. { -------------------------------------------------------------- }
  288. { CLEARBUFFER    setzt den InputBuffer auf 0                     }
  289. { -------------------------------------------------------------- }
  290. Procedure ClearBuffer;
  291. begin
  292.   StartInBuf := EndInBuf;
  293. end;
  294. { -------------------------------------------------------------- }
  295. { NEWINTHANDLER ersetzt den alten Interrupt-Handler und nimmt    }
  296. {               bei einem Interrupt die Zeichen aus dem RHR      }
  297. {               und schreibt sie in den InputBuffer              }
  298. { -------------------------------------------------------------- }
  299. {$F+}
  300. Procedure NewIntHandler; Interrupt;
  301. begin
  302.   DisableInts;
  303.   { Zeichen einlesen, wenn Ereignis = DataReady                  }
  304.   { Ist eigentlich nicht notwenig, wenn im IER nur Ereignisse    }
  305.   { vom Typ DataReady zugelassen wurden.                         }
  306.  
  307.   { Das while .. ist für den FIFO notwendig, da hier auf einmal  }
  308.   { mehrere Bits abgeholt werden können.                         }
  309.   { Ist der FIFO nicht aktiv, dann schadet das while auch nicht. }
  310.   While (Port[ComBase[ComPort] + IIR] and IIR_DRMask)
  311.                          = IIR_DataReady do
  312.     begin
  313.       InputBuffer[EndInBuf] := chr(Port[ComBase[ComPort] + RHR]);
  314.       { EndBuf neu setzen. Erhöhen oder wieder 1 setzen.         }
  315.       EndInBuf := (EndInbuf mod BufSize) + 1;
  316.     end;
  317.   Port[PIC_ICR] := EOI;  { Der PIC kann jetzt den nächsten       }
  318.                          { IRQ bearbeiten                        }
  319.   EnableInts;
  320. end;
  321. {$F-}
  322.  
  323. { -------------------------------------------------------------- }
  324. { SETINT setzt im MCR das Bit OT2, damit überhaupt IRQ's erzeugt }
  325. {        legt im IER fest, bei welchen Ereignissen ein IRQ er-   }
  326. {                    zeugt werden soll.                          }
  327. {        holt den alten Interrupt-Vector und setzt den neuen     }
  328. {                    (NewIntHandler)                             }
  329. {        setzt das dem IRQ entsprechende Bit im PIC_IMR auf 0,   }
  330. {                    damit der PIC den Interrupt weitergibt.     }
  331. {        setzt im MCR, die Bits DTR und RTS                      }
  332. { -------------------------------------------------------------- }
  333. Procedure SetInt;
  334. var reg : byte;
  335. begin
  336.   DisableInts; { verhindern, daß andere Ints dazwischen kommen   }
  337.  
  338.   { Alten Interrupt-Vector holen und neuen Int-Handler setzen    }
  339.   { Die Nummer des Interrupts steht in ComInt[] und ergibt sich  }
  340.   { aus IRQ + 8.                                                 }
  341.   If not isComIntSet Then
  342.       GetIntVec(ComInt[ComPort], OldIntVector);
  343.   SetIntVec(ComInt[ComPort], @NewIntHandler);
  344.  
  345.   { ModemControl-Register mitteilen, daß Interrupts gesendet     }
  346.   { werden können                                                }
  347.   reg := Port[ComBase[ComPort] + MCR] or OT2;
  348.   Port[ComBase[ComPort] + MCR] := reg;
  349.  
  350.   { Im Interrupt-Enable Register (IER) festlegen, bei welchem    }
  351.   { Ereignis ein Interrupt ausgeführt werden soll.               }
  352.   { Hier nur wenn Daten in RHR bereitstehen                      }
  353.   Port[ComBase[ComPort] + IER] := IER_RHR;
  354.  
  355.   { Register im PIC setzen                                       }
  356.   { Im Interrupt-Mask-Register das Bit, das dem IRQ entspricht   }
  357.   { 0 setzen, damit der PIC die IRQ's weiterleitet.              }
  358.   reg :=  byte(not (1 shl ComIRQ[ComPort])); { IRQ3: 1111 0111   }
  359.   Port[PIC_IMR] := Port[PIC_IMR] and reg;
  360.   { Set_DTR_RTS; }        { Wir sind fertig zum Senden           }
  361.   EnableInts;             { Jetzt können Ints ausgeführt werden. }
  362.  
  363.   isComIntSet := true;
  364. end;
  365.  
  366. { -------------------------------------------------------------- }
  367. { RESETINT  macht die in SETINT durchgeführten Einstellungen     }
  368. {           wieder rückgängig.                                   }
  369. { -------------------------------------------------------------- }
  370. Procedure ResetInt;
  371. var reg : byte;
  372. begin
  373.   If not isComIntSet Then EXIT;
  374.  
  375.   { FIFO ausschalten }
  376.   If FIFO_Automatic and Enabled16550(ComPort)
  377.     then Switch16550(ComPort, true, 0);
  378.  
  379.   DisableInts;
  380.  
  381.   { Im Modem-Control-Register DTR (Data Terminal Ready) und      }
  382.   { RTS (Request to send) ausschalten                            }
  383.   reg := byte(not (DTR or RTS));                     { 1111 1100 }
  384.   Port[ComBase[ComPort] + MCR] :=
  385.              Port[ComBase[ComPort] + MCR] and reg;
  386.  
  387.   { dem IRQ entsprechendes Bit im PIC_IMR maskieren (1 setzen)   }
  388.   reg := (1 shl ComIRQ[ComPort]);       { z.B. IRQ4: 0001 0000   }
  389.   Port[PIC_IMR] := Port[PIC_IMR] or reg;
  390.  
  391.   { Im Modem-Control Register verhindern, daß Interrupts         }
  392.   { gesendet werden.                                             }
  393.   reg :=  byte(not OT2);                             { 1111 0111 }
  394.   Port[ComBase[ComPort] + MCR] :=
  395.              Port[ComBase[ComPort] + MCR] and reg;
  396.  
  397.   { Im Interrupt-Enable Register alle Ereignisse disablen        }
  398.   Port[ComBase[ComPort] + IER] := 0;
  399.   { alten InterruptVector wieder setzen                          }
  400.   SetIntVec(ComInt[ComPort], OldIntVector);
  401.   EnableInts;
  402.   isComIntSet := false;
  403. end;
  404.  
  405. { -------------------------------------------------------------- }
  406. { COMMEXITPROC  Sollte das Programm aus irgendeinem Grund        }
  407. {               abstürzen, wird hier schnell noch RESETINT       }
  408. {               ausgeführt.                                      }
  409. { -------------------------------------------------------------- }
  410. {$F+}
  411. Procedure CommExitProc;
  412. begin
  413.   ExitProc := OldExitProc;
  414.   If isComIntSet Then ResetInt;
  415. end;
  416. {$F-}
  417.  
  418. BEGIN
  419.   OldExitProc   := ExitProc;
  420.   ExitProc      := @CommExitProc;
  421.  
  422.   OldIntVector  := nil;
  423. END.