(*////////////////////////////////////////////////////////////////////////////
///                                                                        ///
///         T U R B O  -  P A S C A L  V24-Interrupt-Support V2.00         ///
///                (c) Copyright June 1988 by C.Philipps                   ///
///                                                                        ///
///               (Turbo Pascal V4.0  or higher required)                  ///
///                                                                        ///
//////////////////////////////////////////////////////////////////////////////
///                                                                        ///
///            Low-level interrupt-handling for the serial ports. Speeds   ///
///            up to 115200 bps are supportet, one port at a time.         ///
///            Parts of the basics were taken from Mike Halliday's pop-    ///
///            ular ASYNC-package (Turbo Pascal 3.0, PD).                  ///
///                                                                        ///
///       This module is hereby donated to the public domain.              ///
///                                                                        ///
///       Christian Philipps                                               ///
///       Dsseldorfer Str. 316                                            ///
///       4130 Moers 1                                                     ///
///       West-Germany                                                     ///
///                                                                        ///
///       Last modified: 07/89                                             ///
///                                                                        ///
////////////////////////////////////////////////////////////////////////////*)

{$R-,S-,I-,D-,F-,V-,B-,N-,L- }

UNIT XV24;

INTERFACE

USES DOS;


{$ifdef ComTypedefiniert} {$else}
TYPE   ComType      = (Com1,Com2,Com3,Com4,Com5,Com6);
{$define ComTypedefiniert}
{$endif}


TYPE  { ComType      = (Com1,Com2,Com3,Com4,Com5,Com6);}
       BaudType     = (b110,b150,b300,b600,b1200,b2400,b4800,b9600,b19200,
                       b38400,b57600,b115200);
       ParityType   = (Space,Odd,Mark,Even,None);
       DataBitsType = (d5,d6,d7,d8);
       StopBitsType = (s1,s2);

CONST  V24Timeout   : BOOLEAN = FALSE;  {SendByte-Timeout}
       IntMasks     : ARRAY[Com1..Com6] OF WORD  = ($EF,$F7,$fb,$F7,$EF,$F7);
       IntVect      : ARRAY[Com1..Com6] OF BYTE  = ($0C,$0B,$0a,$0B,$0C,$0B);

       {com3 auf $fb/$0a==irq 2 geaendert, com4... noch nicht geaendert...}

(*
VAR    V24TP        : WORD;         {Buffer Tail-Pointer
                                     Im Interface-Teil, da zur Ereignis-
                                     steuerung im Multi-Tasking bentigt.}
                                     *)
var    ComBaseAdr   : ARRAY[Com1..Com6] OF WORD;

FUNCTION  V24DataAvail(Com:integer):BOOLEAN;
FUNCTION  V24DataReady(Com:integer):BOOLEAN;
FUNCTION  V24GetByte(Com:integer):BYTE;

PROCEDURE InitCom_OLD(ComPort:ComType;
                  Baudrate:BaudType;Parity:ParityType;
                  Bits:DataBitsType;Stop:StopBitsType);
PROCEDURE InitCom(Com:integer;
                  Baudrate:BaudType;Parity:ParityType;
                  Bits:DataBitsType;Stop:StopBitsType);

PROCEDURE DisableCom;
PROCEDURE SendByte(Com:integer;Data:BYTE);

{$ifdef simulate}
function getsendbuffer(Com:integer;var s:string):boolean;
procedure setreadbuffer(Com:integer;s:string);
{$endif}

{=============================================================================}

IMPLEMENTATION

CONST  Regs       : Registers =
       (AX:0;BX:0;CX:0;DX:0;BP:0;SI:0;DI:0;DS:0;ES:0;FLAGS:0);
       RBR        = $00;          {xF8 Receive Buffer Register            }
       THR        = $00;          {xF8 Transmitter Holding Register       }
       IER        = $01;          {xF9 Interrupt Enable Register          }
       IIR        = $02;          {xFA Interrupt Identification Register  }
       LCR        = $03;          {xFB Line Control Register              }
       MCR        = $04;          {xFC Modem Control Register             }
       LSR        = $05;          {xFD Line Status Register               }
       MSR        = $06;          {xFE Modem Status Register              }
                                  {--- if LCR Bit 7 = 1  ---              }
       DLL        = $00;          {xF8 Divisor Latch Low Byte             }
       DLH        = $01;          {xF9 Divisor Latch Hi  Byte             }
       CMD8259    = $20;          {Interrupt Controller Command Register  }
       IMR8259    = $21;          {Interrupt Controller Mask Register     }
                                  {Should be evaluated by any higher-level
                                   send-routine}
       LoopLimit  = 1000;         {When does a timeout-error occur        }
       V24BuffSize= 8192;         { Ringpuffer 8 KB }

type BufferType = ARRAY[0..V24BuffSize] OF BYTE;

VAR    BiosComBaseAdr : ARRAY[Com1..Com2] OF WORD ABSOLUTE $0040:$0000;
       ActivePort : ComType;
       { The Com-Port base adresses are taken from the BIOS data area }

       ComBase    : WORD;         {Hardware Com-Port Base Adress          }
       OldV24     : Pointer;
       V24TP      : WORD;         {Buffer Head-Pointer                    }
       V24HP      : WORD;         {Buffer Head-Pointer                    }
       V24BuffEnd : WORD;         {Buffer End-Adress                      }
       V24Buff    : BufferType;

       AComBase    : WORD;         {Hardware Com-Port Base Adress          }
       AOldV24     : Pointer;
       AV24TP      : WORD;         {Buffer Head-Pointer                    }
       AV24HP      : WORD;         {Buffer Head-Pointer                    }
       AV24BuffEnd : WORD;         {Buffer End-Adress                      }
       AV24Buff    : BufferType;

       BComBase    : WORD;         {Hardware Com-Port Base Adress          }
       BOldV24     : Pointer;
       BV24TP      : WORD;         {Buffer Head-Pointer                    }
       BV24HP      : WORD;         {Buffer Head-Pointer                    }
       BV24BuffEnd : WORD;         {Buffer End-Adress                      }
       BV24Buff    : BufferType;

       CComBase    : WORD;         {Hardware Com-Port Base Adress          }
       COldV24     : Pointer;
       CV24TP      : WORD;         {Buffer Head-Pointer                    }
       CV24HP      : WORD;         {Buffer Head-Pointer                    }
       CV24BuffEnd : WORD;         {Buffer End-Adress                      }
       CV24Buff    : BufferType;


       DComBase    : WORD;         {Hardware Com-Port Base Adress          }
       DOldV24     : Pointer;
       DV24TP      : WORD;         {Buffer Head-Pointer                    }
       DV24HP      : WORD;         {Buffer Head-Pointer                    }
       DV24BuffEnd : WORD;         {Buffer End-Adress                      }
       DV24Buff    : BufferType;

       OExitHandler : Pointer;    {Save-Area fr Zeiger auf Org.-Exit-Proc}


{============================ lokale Routinen ================================}

CONST CURRENTCOM : word = 0;
      DataReady  : array[0..4] of byte = (0,0,0,0,0);


{-------------------------Simulations-Routinen-------------------------------}

{$ifdef simulate}
const simbuffermax   = 400;         {Groesse des Simulationssendepuffers}
var   sendbuffer     : array[1..4,1..simbuffermax] of char;
const sendbuffersize : array[1..4] of integer = (0,0,0,0);


function getsendbuffer(Com:integer;var s:string):boolean;
var i,imax : integer;
begin
    getsendbuffer:=(sendbuffersize[Com]>0);
    if sendbuffersize[Com]=0 then s:=''
    else begin
         if sendbuffersize[com]>255 then imax:=255
                                    else imax:=sendbuffersize[Com];
         s:='';
         for i:=1 to imax do s:=s+sendbuffer[Com,i];
         if imax<sendbuffersize[Com] then begin
            for i:=imax+1 to sendbuffersize[com] do
                sendbuffer[i-imax]:=sendbuffer[i];
            end;
         sendbuffersize[Com] := sendbuffersize[com]-imax;
         end;
end;

procedure setreadbuffer(Com:integer;s:string);
    procedure setbuffer(var TP   : word;       HP      : word;
                        var Buff : BufferType; BuffEnd : word);
    var i : integer;
        tpi : word;
    begin
      for i:=1 to length(s) do begin
          tpi:=TP;
          Mem[DSeg:TP]:=ord(s[i]);
          inc(tpi);
          if tpi>BuffEnd then tpi:=Ofs(Buff);
          if tpi<>HP then TP:=tpi;
          if s[i]=#13 then DataReady[Com] := 1;
          end;
    end;
begin
       if Com=0 then  SetBuffer( V24TP, V24HP, V24Buff, V24BuffEnd)
  else if Com=1 then  SetBuffer(AV24TP,AV24HP,AV24Buff,AV24BuffEnd)
  else if Com=2 then  SetBuffer(BV24TP,BV24HP,BV24Buff,BV24BuffEnd)
  else if Com=3 then  SetBuffer(CV24TP,CV24HP,CV24Buff,CV24BuffEnd)
  else if Com=4 then  SetBuffer(DV24TP,DV24HP,DV24Buff,DV24BuffEnd);
end;
{$endif}

{-----------------------Ende Simulations-Routinen----------------------------}

procedure antworten; far;
begin
     DataReady[CurrentCom] := 1;
end;

PROCEDURE V24Int; {external;} assembler;
asm
{ benutzt werden:
         V24HP      : WORD
         V24TP      : WORD
         V24BuffEnd : WORD
         V24Buff    : BYTE
         ComBase    : WORD     }
{;  this routine is executed whenever a character arrives    }
         push ds                           {  save registers             }
         push ax
         push bx
         push cx
         push dx
         mov ax,SEG @DATA                  {  Datensegment setzen        }
         mov ds,ax

         mov  bx,V24TP                     {  ds:bx -> next free slot    }
         mov  dx,ComBase                   {  dx = port base-address     }
         in   al,dx                        {  RBR -> al                  }
         mov  byte ptr [bx],al             {  move byte into buffer      }
         inc  bx                           {  pointer to next slot       }
         cmp  bx,V24BuffEnd                {  past the end of the buffer?}
         jle  @L1                          {  no                         }
         mov  bx,OFFSET V24Buff            {  yes, so wrap around        }

@L1:     cmp  bx,V24HP                     {  TP=HP --> overflow!        }
         jz   @L2                          {  yes, ignore character      }
         mov  V24TP,bx                     {  no, save new tail pointer  }

@L2:     mov  cl,al
(*       mov  al,20H                       {  EOI -> 8259                }
         out  20H,al  *)
         cmp  cl,0D
         jne  @L3
{         mov  CURRENTCOM,00  }
{----------------------------------------------------}
{         call antworten;    }           { anstelle eines call-Aufrufes: }
         mov  bx, OFFSET DataReady
         mov  byte ptr [bx],01             {  move byte into buffer      }
{----------------------------------------------------}

@L3:     mov  al,20H                       {  EOI -> 8259                }
         out  20H,al
{@L3: }  pop  dx                           {  restore registers          }
         pop  cx
         pop  bx
         pop  ax
         pop  ds
         iret
END;

PROCEDURE AV24Int; {external;} assembler;
asm
{ benutzt werden:
         AV24HP      : WORD
         AV24TP      : WORD
         AV24BuffEnd : WORD
         AV24Buff    : BYTE
         AComBase    : WORD     }
{;  this routine is executed whenever a character arrives    }
         push ds                           {  save registers             }
         push ax
         push bx
         push cx
         push dx
         mov ax,SEG @DATA                  {  Datensegment setzen        }
         mov ds,ax

         mov  bx,AV24TP                    {  ds:bx -> next free slot    }
         mov  dx,AComBase                  {  dx = port base-address     }
         in   al,dx                        {  RBR -> al                  }
         mov  byte ptr [bx],al             {  move byte into buffer      }
         inc  bx                           {  pointer to next slot       }
         cmp  bx,AV24BuffEnd               {  past the end of the buffer?}
         jle  @L1                          {  no                         }
         mov  bx,OFFSET AV24Buff           {  yes, so wrap around        }

@L1:     cmp  bx,AV24HP                    {  TP=HP --> overflow!        }
         jz   @L2                          {  yes, ignore character      }
         mov  AV24TP,bx                    {  no, save new tail pointer  }

@L2:     mov  cl,al
         cmp  cl,0D
         jne  @L3
{         mov  CURRENTCOM,01  }
{----------------------------------------------------}
{         call antworten;    }           { anstelle eines call-Aufrufes: }
         mov  bx, OFFSET DataReady
         inc  bx
         mov  byte ptr [bx],01             {  move byte into buffer      }
{----------------------------------------------------}

@L3:     mov  al,20H                       {  EOI -> 8259                }
         out  20H,al
{@L3: }  pop  dx                           {  restore registers          }
         pop  cx
         pop  bx
         pop  ax
         pop  ds
         iret
END;

PROCEDURE BV24Int; {external;} assembler;
asm
{ benutzt werden:
         BV24HP      : WORD
         BV24TP      : WORD
         BV24BuffEnd : WORD
         BV24Buff    : BYTE
         BComBase    : WORD     }
{;  this routine is executed whenever a character arrives    }
         push ds                           {  save registers             }
         push ax
         push bx
         push cx
         push dx
         mov ax,SEG @DATA                  {  Datensegment setzen        }
         mov ds,ax

         mov  bx,BV24TP                    {  ds:bx -> next free slot    }
         mov  dx,BComBase                  {  dx = port base-address     }
         in   al,dx                        {  RBR -> al                  }
         mov  byte ptr [bx],al             {  move byte into buffer      }
         inc  bx                           {  pointer to next slot       }
         cmp  bx,BV24BuffEnd               {  past the end of the buffer?}
         jle  @L1                          {  no                         }
         mov  bx,OFFSET BV24Buff           {  yes, so wrap around        }

@L1:     cmp  bx,BV24HP                    {  TP=HP --> overflow!        }
         jz   @L2                          {  yes, ignore character      }
         mov  BV24TP,bx                    {  no, save new tail pointer  }

@L2:     mov  cl,al
         cmp  cl,0D
         jne  @L3
{         mov  CURRENTCOM,02  }
{----------------------------------------------------}
{         call antworten;    }           { anstelle eines call-Aufrufes: }
         mov  bx, OFFSET DataReady
         inc  bx
         inc  bx
         mov  byte ptr [bx],01             {  move byte into buffer      }
{----------------------------------------------------}

@L3:     mov  al,20H                       {  EOI -> 8259                }
         out  20H,al
{@L3: }  pop  dx                           {  restore registers          }
         pop  cx
         pop  bx
         pop  ax
         pop  ds
         iret
END;

PROCEDURE CV24Int; {external;} assembler;
asm
{ benutzt werden:
         CV24HP      : WORD
         CV24TP      : WORD
         CV24BuffEnd : WORD
         CV24Buff    : BYTE
         CComBase    : WORD     }
{;  this routine is executed whenever a character arrives    }
         push ds                           {  save registers             }
         push ax
         push bx
         push cx
         push dx
         mov ax,SEG @DATA                  {  Datensegment setzen        }
         mov ds,ax

         mov  bx,CV24TP                    {  ds:bx -> next free slot    }
         mov  dx,CComBase                  {  dx = port base-address     }
         in   al,dx                        {  RBR -> al                  }
         mov  byte ptr [bx],al             {  move byte into buffer      }
         inc  bx                           {  pointer to next slot       }
         cmp  bx,CV24BuffEnd               {  past the end of the buffer?}
         jle  @L1                          {  no                         }
         mov  bx,OFFSET CV24Buff           {  yes, so wrap around        }

@L1:     cmp  bx,CV24HP                    {  TP=HP --> overflow!        }
         jz   @L2                          {  yes, ignore character      }
         mov  CV24TP,bx                    {  no, save new tail pointer  }

@L2:     mov  cl,al
         cmp  cl,0D
         jne  @L3
{         mov  CURRENTCOM,03  }
{----------------------------------------------------}
{         call antworten;    }           { anstelle eines call-Aufrufes: }
         mov  bx, OFFSET DataReady
         inc  bx
         inc  bx
         inc  bx
         mov  byte ptr [bx],01             {  move byte into buffer      }
{----------------------------------------------------}

@L3:     mov  al,20H                       {  EOI -> 8259                }
         out  20H,al
{@L3: }  pop  dx                           {  restore registers          }
         pop  cx
         pop  bx
         pop  ax
         pop  ds
         iret
END;

PROCEDURE DV24Int; {external;} assembler;
asm
{ benutzt werden:
         DV24HP      : WORD
         DV24TP      : WORD
         DV24BuffEnd : WORD
         DV24Buff    : BYTE
         DComBase    : WORD     }
{;  this routine is executed whenever a character arrives    }
         push ds                           {  save registers             }
         push ax
         push bx
         push cx
         push dx
         mov ax,SEG @DATA                  {  Datensegment setzen        }
         mov ds,ax

         mov  bx,DV24TP                    {  ds:bx -> next free slot    }
         mov  dx,DComBase                  {  dx = port base-address     }
         in   al,dx                        {  RBR -> al                  }
         mov  byte ptr [bx],al             {  move byte into buffer      }
         inc  bx                           {  pointer to next slot       }
         cmp  bx,DV24BuffEnd               {  past the end of the buffer?}
         jle  @L1                          {  no                         }
         mov  bx,OFFSET DV24Buff           {  yes, so wrap around        }

@L1:     cmp  bx,DV24HP                    {  TP=HP --> overflow!        }
         jz   @L2                          {  yes, ignore character      }
         mov  DV24TP,bx                    {  no, save new tail pointer  }

@L2:     mov  cl,al
         cmp  cl,0D
         jne  @L3
         mov  CURRENTCOM,03
         call antworten;
@L3:     mov  al,20H                       {  EOI -> 8259                }
         out  20H,al
{@L3: }  pop  dx                           {  restore registers          }
         pop  cx
         pop  bx
         pop  ax
         pop  ds
         iret
END;

{-----------------------------------------------------------------------------}

PROCEDURE ClearPendingInterrupts(Base:word);

VAR  N : BYTE;

BEGIN {ClearPendingInterrupts}
    {$ifndef simulate}
      WHILE (PORT[Base+IIR] AND 1) = 0 DO  {While Interrupts are pending}
      BEGIN
        N := PORT[Base+LSR];               {Read Line Status}
        N := PORT[Base+MSR];               {Read Modem Status}
        N := PORT[Base+RBR];               {Read Receive Buffer Register}
        PORT[CMD8259] := $20;              {End of Interrupt}
      END;
    {$endif}
END;  {ClearPendingInterrupts}

{======================== extern verfgbare Routinen =========================}

{-----------------------------------------------------------------------------}


FUNCTION V24DataAvail(Com:integer):BOOLEAN;

{ This function checks, whether there are characters in the buffer }

BEGIN {V24DataAvail}
       if Com=0 then V24DataAvail := ( V24HP <> V24TP)
  else if Com=1 then V24DataAvail := (AV24HP <> AV24TP)
  else if Com=2 then V24DataAvail := (BV24HP <> BV24TP)
  else if Com=3 then V24DataAvail := (CV24HP <> CV24TP)
  else if Com=4 then V24DataAvail := (DV24HP <> DV24TP);
END;  {V24DataAvail}

{-----------------------------------------------------------------------------}


FUNCTION V24DataReady(Com:integer):BOOLEAN;

{This function checks, whether the characters in the buffer are a full command}

BEGIN {V24DataReady}
  if (Com>=1) and (Com<5) then
       V24DataReady := (DataReady[Com]=1)
  else V24DataReady := false;
END;  {V24DataReady}

{-----------------------------------------------------------------------------}

FUNCTION V24GetByte(Com:integer):BYTE;

{ Take a byte out of the ring-buffer and return it to the caller.
  This function assumes, that the application has called V24DataAvail
  before to assure, that there are characters available!!!!
  The ISR only reads the current head-pointer value, so this routine
  may modify the head-pointer with interrupts enabled. }

    FUNCTION GetByte(var HP:word;var Buff: BufferType;BuffEnd:word):Byte;
    var B : Byte;
    begin
    B := Mem[DSeg:HP];
    Inc(HP);
    IF HP > BuffEnd THEN HP := Ofs(Buff);
    GetByte := B;
    if B=13 then DataReady[Com] := 0;
    end;
BEGIN {V24GetByte}
       if Com=0 then V24GetByte := GetByte( V24HP, V24Buff, V24BuffEnd)
  else if Com=1 then V24GetByte := GetByte(AV24HP,AV24Buff,AV24BuffEnd)
  else if Com=2 then V24GetByte := GetByte(BV24HP,BV24Buff,BV24BuffEnd)
  else if Com=3 then V24GetByte := GetByte(CV24HP,CV24Buff,CV24BuffEnd)
  else if Com=4 then V24GetByte := GetByte(DV24HP,DV24Buff,DV24BuffEnd);
END;  {V24GetByte}

{-----------------------------------------------------------------------------}

VAR   Count:BYTE;

PROCEDURE SendByte(Com:integer;Data:BYTE);
var Base : word;
BEGIN {SendByte}
  Count := 0;
  V24Timeout := FALSE;
  Base:= ComBase;
       if Com=1 then Base:=AComBase
  else if Com=2 then Base:=BComBase
  else if Com=3 then Base:=CComBase
  else if Com=4 then Base:=DComBase;
 {$ifdef simulate}
  if sendbuffersize[com]<simbuffermax then begin
     inc(sendbuffersize[com]);
     sendbuffer[com,sendbuffersize[com]] := chr(data);
     end;
 {$else}
  IF Base > 0
     THEN BEGIN
            REPEAT
              Count := SUCC(Count);
            UNTIL ((PORT[Base+LSR] AND $20) <> 0) OR (Count > LoopLimit);
            IF Count > LoopLimit
               THEN V24Timeout := TRUE
               ELSE PORT[Base+THR] := Data;
          END;
 {$endif}
END;  {SendByte}

{-----------------------------------------------------------------------------}

CONST BaudConst   : ARRAY[b110..b115200] OF WORD =
                    ($417,$300,$180,$C0,$60,$30,$18,$0C,$06,$03,$02,$01);
      ParityConst : ARRAY[Space..None] OF BYTE =
                    ($38,$08,$28,$18,$00);
      BitsConst   : ARRAY[d5..d8] OF BYTE =
                    ($00,$01,$02,$03);
      StopConst   : ARRAY[s1..s2] OF BYTE =
                    ($00,$04);

PROCEDURE InitCom_intern(
            ComPort : ComType;
            Baudrate: BaudType;
            Parity  : ParityType;
            Bits    : DataBitsType;
            Stop    : StopBitsType;
        var HP      : word;
        var TP      : word;
        var BuffEnd : word;
        var Base    : word;
        var Buff    : BufferType;
        var OldVec  : Pointer;
            NewInt  : Pointer );
BEGIN {InitCom}
     HP       := Ofs(Buff);
     TP       := HP;
     BuffEnd  := HP+V24BuffSize;
     FillChar(Buff,Succ(V24BuffSize),#0);
     V24Timeout := FALSE;                          {Reset Timeout-Marker}
    {$ifdef simulate}
     Base := Ord(ComPort);
    {$else}
     Base := ComBaseAdr[ComPort];                  {Get Com-Port base adress}
     ClearPendingInterrupts(Base);
     GetIntVec(IntVect[ComPort],OldVec);
     SetIntVec(IntVect[ComPort],Newint);
                                                   {Install interrupt routine}
     INLINE($FA);                                  {CLI}
     PORT[Base+LCR] := $80;                        {Adress Divisor Latch}
     PORT[Base+DLH] := Hi(BaudConst[Baudrate]);    {Set Baud rate}
     PORT[Base+DLL] := Lo(BaudConst[Baudrate]);
     PORT[Base+LCR] := ($00 OR ParityConst[Parity] {Setup Parity}
                            OR BitsConst[Bits]     {Setup number of databits}
                            OR StopConst[Stop]);   {Setup number of stopbits}
     PORT[Base+MCR] := $0B;                        {Set RTS,DTR,OUT2}
   (*
     PORT[Base+MCR] := $1B;                        {Set RTS,DTR,OUT2,Loop}
   *)
     PORT[Base+IER] := $01;                {Enable Data-Available Interrupts}
     PORT[IMR8259] := PORT[IMR8259] AND
                      IntMasks[ComPort];           {Enable V24-Interrups}
     INLINE($FB);                                     {STI}
    {$endif}
END;  {InitCom}



PROCEDURE InitCom_old(ComPort:ComType;Baudrate:BaudType;Parity:ParityType;
                  Bits:DataBitsType;Stop:StopBitsType);
BEGIN {InitCom}
  InitCom_intern(ComPort,Baudrate,Parity,Bits,Stop,
                 V24HP,V24TP,V24BuffEnd,ComBase,V24Buff,OldV24,@V24Int);
  ActivePort := ComPort;                       {Keep Active-Port for EOI}
END;  {InitCom}

PROCEDURE InitCom(Com:integer;
                  Baudrate:BaudType;Parity:ParityType;
                  Bits:DataBitsType;Stop:StopBitsType);
BEGIN {InitCom1}
  if Com=1 then
  InitCom_intern(Com1,Baudrate,Parity,Bits,Stop,
               AV24HP,AV24TP,AV24BuffEnd,AComBase,AV24Buff,AOldV24,@AV24Int)
  else if Com=2 then
  InitCom_intern(Com2,Baudrate,Parity,Bits,Stop,
               BV24HP,BV24TP,BV24BuffEnd,BComBase,BV24Buff,BOldV24,@BV24Int)
  else if Com=3 then
  InitCom_intern(Com3,Baudrate,Parity,Bits,Stop,
               CV24HP,CV24TP,CV24BuffEnd,CComBase,CV24Buff,COldV24,@CV24Int)
  else if Com=4 then
  InitCom_intern(Com4,Baudrate,Parity,Bits,Stop,
               DV24HP,DV24TP,DV24BuffEnd,DComBase,DV24Buff,DOldV24,@DV24Int);
END;  {InitCom1}

{-----------------------------------------------------------------------------}

procedure Disable_intern(var Base:word;Old:pointer;PortNr:ComType); far;
begin
 {$ifdef simulate}
  Base:=0;
 {$else}
  IF Base = 0 THEN Exit;
  INLINE($FA);                              {CLI}
  PORT[Base+MCR] := 00;                     {Disable Interrupts, Reset MCR}
  PORT[IMR8259]  := PORT[IMR8259] OR $18;   {Disable Interrupt Level 3 and 4}
  PORT[Base+IER] := 0;                      {Disable 8250-Interrupts}
  ClearPendingInterrupts(Base);             {Clean up}
  Base := 0;                                {Reset Combase}
  SetIntVec(IntVect[PortNr],Old);           {Reset old IV}
  INLINE($FB);                              {STI}
 {$endif}
end;

PROCEDURE DisableCom;
BEGIN {DisableCom}
    Disable_intern( ComBase, OldV24,ActivePort);
    Disable_intern(AComBase,AOldV24,Com1);
    Disable_intern(BComBase,BOldV24,Com2);
    Disable_intern(CComBase,COldV24,Com3);
    Disable_intern(DComBase,DOldV24,Com4);
END;  {DisableCom}

{-----------------------------------------------------------------------------}

{$F+}
PROCEDURE V24ExitProc;

BEGIN {V24ExitProc}
  ExitProc := OExitHandler;                 { alten Exit-Handler reaktivieren }
  DisableCom;
END;  {V24ExitProc}
{$F-}

{-----------------------------------------------------------------------------}

BEGIN {Initialisation}
  {Grund-Init, damit irrtmliche Aufrufe von V24DataAvail nicht zu
   endlosen Ausgaben von Speicherschrott fhren!}
  Move(BiosComBaseAdr,ComBaseAdr[Com1],SizeOf(BiosComBaseAdr));
{  Move(BiosComBaseAdr,ComBaseAdr[Com3],SizeOf(BiosComBaseAdr));}
{  Move(BiosComBaseAdr,ComBaseAdr[Com5],SizeOf(BiosComBaseAdr));}

  ComBaseAdr[Com3]:=$3e8;
  {  ^das Olli-BIOS mag an $40:4 ff. keine Eintraege...}
  (* ComBaseAdr[4]:=$2e8 *)



  ComBase     := 0;
  V24HP       := Ofs(V24Buff);
  V24TP       := V24HP;
  V24BuffEnd  := V24HP+V24BuffSize;

  AComBase     := 0;
  AV24HP       := Ofs(AV24Buff);
  AV24TP       := AV24HP;
  AV24BuffEnd  := AV24HP+V24BuffSize;

  BComBase     := 0;
  BV24HP       := Ofs(BV24Buff);
  BV24TP       := BV24HP;
  BV24BuffEnd  := BV24HP+V24BuffSize;

  CComBase     := 0;
  CV24HP       := Ofs(CV24Buff);
  CV24TP       := CV24HP;
  CV24BuffEnd  := CV24HP+V24BuffSize;

  DComBase     := 0;
  DV24HP       := Ofs(DV24Buff);
  DV24TP       := DV24HP;
  DV24BuffEnd  := DV24HP+V24BuffSize;

  OExitHandler := ExitProc;
  ExitProc     := @V24ExitProc;

END.  {Initialisation}

(*////////////////////////////////////////////////////////////////////////////
///                       E N D   O F   M O D U L E                        ///
////////////////////////////////////////////////////////////////////////////*)
