Unit ApSame;

{$IFNDEF OS2}
{$F+}

{$ENDIF}

{*********************************************************}
{*                                                       *}
{*                    APSAME.PAS 1.0                     *}
{*                                                       *}
{*     Copyright (c) Konstantin Klyagin, 1996.           *}
{*                   expecialy for Tornado BBS system    *}
{*                                                       *}
{*********************************************************}

Interface

Uses
  Crc32;

Var
  AsyncStatus                            : Word;

Type

  CharArray = Array [0..MaxInt] Of Char;

  {For internal date/time manipulations}
  Date = LongInt;
  Time = LongInt;
  DateTimeRec =
  Record
    D : Date;
    T : Time;
  End;
  DayType = (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);

  PS2Mode = (PS2On, PS2Off, PS2Auto, PS2Ignore);

  StringPtr = ^String;
  CharSet = Set Of Char;

  ComNameType = (Com1,  Com2,  Com3,  Com4,  Com5,  Com6,  Com7,  Com8,
  Com9,  Com10, Com11, Com12, Com13, Com14, Com15, Com16,
  Com17, Com18, Com19, Com20, Com21, Com22, Com23, Com24,
  Com25, Com26, Com27, Com28, Com29, Com30, Com31, Com32,
  Com33, Com34, Com35, Com36);

  PortRecPtr = ^PortRec;
  ParityType = (NoParity, OddParity, EvenParity, MarkParity, SpaceParity);
  DataBitType = 5..8;
  StopBitType = 1..2;
  FlowState = (fsOff, fsClear, fsTransWait, fsRecWait, fsAllWait);

  {Line options record}
  LineOptionRecord = Record
                       Parity        : ParityType;
                       DataBits      : DataBitType;
                       StopBits      : StopBitType;
                       Options       : Word;
                       InSize        : Word;
                       OutSize       : Word;
                     End;

  InitPortProc = Procedure (Var P : PortRecPtr; ComName : ComNameType;
                           Baud : LongInt; Parity : ParityType;
                           DataBits : DataBitType; StopBit : StopBitType;
                           InSize, OutSize : Word;
                           Options : Word);
  InitPortKeepProc = Procedure (Var P : PortRecPtr; ComName : ComNameType;
                               InSize, OutSize : Word);
  DonePortProc = Procedure (Var P : PortRecPtr);
  SetUartProc = Procedure (ComName : ComNameType; NewBase : Word;
                          NewIrq, NewVector : Byte);
  SetLineProc = Procedure (P : PortRecPtr; Baud : LongInt; Parity : ParityType;
                          DataBits : DataBitType; StopBits : StopBitType);
  GetLineProc = Procedure (P : PortRecPtr; Var Baud : LongInt;
                          Var Parity : ParityType;
                          Var DataBits : DataBitType;
                          Var StopBits : StopBitType;
                          FromHardware : Boolean);
  SetModemProc = Procedure (P : PortRecPtr; SetDTR, SetRTS : Boolean);
  GetModemProc = Procedure (P : PortRecPtr; Var DTR, RTS : Boolean);
  GetCharProc = Procedure (P : PortRecPtr; Var C : Char);
  PeekCharProc = Procedure (P : PortRecPtr; Var C : Char; PeekAhead : Word);
  PutCharProc = Procedure (P : PortRecPtr; C : Char);
  StartTransmitterProc = Procedure (P : PortRecPtr);
  CharReadyFunc = Function (P : PortRecPtr) : Boolean;
  TransReadyFunc = Function (P : PortRecPtr) : Boolean;
  SendBreakProc = Procedure (P : PortRecPtr);
  ActivatePortProc = Procedure (P : PortRecPtr; Restore : Boolean);
  SavePortProc = Procedure (P : PortRecPtr; Var PSR);
  GotErrorProc = Procedure (P : PortRecPtr; StatusCode : Word);

  {Core types added for alternate device layers}
  UpdateLineStatusFunc = Function (P : PortRecPtr) : Byte;
  UpdateModemStatusFunc = Function (P : PortRecPtr) : Byte;
  FlowSetProc = Procedure (P : PortRecPtr; Enable : Boolean;
                          BufferFull, BufferResume : Word;
                          Options : Word);
  FlowGetFunc = Function (P : PortRecPtr) : FlowState;
  FlowCtlProc = Procedure (P : PortRecPtr; OnChar, OffChar : Char;
                          Resume : Boolean);
  BufferStatusProc = Procedure (P : PortRecPtr;
                               Var InFree, OutFree, InUsed, OutUsed : Word);
  BufferFlushProc = Procedure (P : PortRecPtr; FlushIn, FlushOut: Boolean);

  {Procedure type for error handler}
  AsyncErrorProc = Procedure (P : Pointer; Var StatusCode : Word);

  BPtr = ^Byte;
  AbortFunc = Function : Boolean;

  PortSaveRec = Record
                  PicMask : Byte;
                  IER     : Byte;
                  MCR     : Byte;
                  LCR     : Byte;
                  BRLR    : Byte;
                  BRHR    : Byte;
                  FIFO    : Byte;
                  Trigger : Byte;
                  Vector  : Pointer;
                End;

  PortRec = Record
              BaseAddr      : Word;             {Base IO addr of UART}
              Flags         : Word;             {Option flags for port options}
              InBuffLen     : Word;             {Length of input buffer}
              InBuffCount   : Word;             {Current # of chars in buffer}
              OutBuffLen    : Word;             {Length of output buffer}
              OutBuffCount  : Word;             {Current # of chars in buffer}
              LostCharCount : Word;             {Number of lost characters}
              SWFFull       : Word;             {Hi-water mark for xoff}
              SWFResume     : Word;             {Lo-water mark for xon}
              HWFFull       : Word;             {Hi-water mark for auto-handshaking off}
              HWFResume     : Word;             {Lo-water mark for auto-handshaking on}
              CurBaud       : LongInt;          {Baud rate}
              InBuff        : BPtr;             {Addr of input buffer}
              InHead        : BPtr;             {Addr of current head}
              InTail        : BPtr;             {Addr of current tail}
              InBuffEnd     : BPtr;             {Addr of end of buffer}
              OutBuff       : BPtr;             {Addr of output buffer}
              OutHead       : BPtr;             {Addr of current head}
              OutTail       : BPtr;             {Addr of current tail}
              OutBuffEnd    : BPtr;             {Addr of end-of-buffer}
              StatBuff      : BPtr;             {Addr of status buffer}
              StatHead      : BPtr;             {Addr of current status head}
              StatTail      : BPtr;             {Addr of current status tail}
              StatBuffEnd   : BPtr;             {Addr of end of status buffer}
              PortName      : ComNameType;      {"Standard" name (COM1,COM2...)}
              Vector        : Byte;             {Vector number of UART interrupt}
              IrqNumber     : Byte;             {IRQ number for this port}
              IntMask       : Byte;             {Current UART interrupt enable}
              CurrentPort   : Byte;             {Current active port number}
              ISREntryPoint : Byte;             {Entry point number into APUART.ASM}
              ModemStatus   : Byte;             {Current modem status}
              ModemControl  : Byte;             {Current modem control value}
              LineStatus    : Byte;             {Current line status}
              LineControl   : Byte;             {Current line control value}
              SWFState      : Byte;             {Sofware flow control options}
              SWFGotXoff    : Boolean;          {True if Xoff char received}
              SWFSentXoff   : Boolean;          {True if Xoff char sent}
              SWFOnChar     : Char;             {SW flow on character (def = $11, Xon)}
              SWFOffChar    : Char;             {SW flow off character (def = $13, Xoff)}
              BreakReceived : Boolean;          {True in break received}
              TxReady       : Boolean;          {True if transmitter is available}
              TxInts        : Boolean;          {True if using transmit interrupts}
              TxIntsActive  : Boolean;          {True if transmit ints are active}
              Buffered      : Boolean;          {True if using buffer serial I/O}
              UseStatusBuffer : Boolean;        {True if using status buffer}
              OldUart       : Boolean;          {True if UART is 8250 or 8250B}
              CurParity     : ParityType;       {Parity}
              CurDataBits   : DataBitType;      {Data bits}
              CurStopBits   : StopBitType;      {Stop bits}
              SaveChar      : Char;             {Last known char (used internally only)}
              LastXmitError : Byte;             {Reason for last failed xmit}
              HWFTransMask  : Byte;             {Mask to XOR modem status bits to zero}
              HWFTransHonor : Byte;             {Mask of required modem status bits}
              HWFRecMask    : Byte;             {Mask of "on" modem status bits}
              HWFRecHonor   : Byte;             {Mask of modem status bits we care about}
              HWFRemoteOff  : Boolean;          {True if we have turned off the remote}
              ISRActive     : Boolean;          {True if in debugging mode}
              ProtocolActive : Boolean;         {True if this port is doing a protocol}
              FaxActive     : Boolean;          {True if this port is doing a fax}
              DoneProc      : DonePortProc;     {DonePort proc for this port}
              ErrorProc     : AsyncErrorProc;   {Pointer to error procedure}
              ErrorData     : Pointer;          {Pointer passed to error routine}
              UserAbort     : AbortFunc;        {Hook for user (keyboard) abort}
              OrigPortState : PortSaveRec;      {Record for saving init port config}
            End;

Const
  ParityString : Array [ParityType] Of String [5] = (
  'None', 'Odd', 'Even', 'Mark', 'Space');

  {8250 register designations}
  THreg  = 0;                        {Transmit hold}
  RDreg  = 0;                        {Read data}
  BRLreg = 0;                        {Baud rate least sig}
  BRHreg = 1;                        {Baud rate most sig}
  IEreg  = 1;                        {Int enable reg}
  IIDreg = 2;                        {Int ident reg}
  LCreg  = 3;                        {Line control}
  MCreg  = 4;                        {Modem control}
  LSreg  = 5;                        {Line status}
  MSreg  = 6;                        {Modem status}
  Sreg   = 7;                        {Scratch register}

  {Line control bit masks}
  WordLen0Mask     = $01;        {Word length select 0}
  WordLen1Mask     = $02;        {Word length select 1}
  StopBitsMask     = $04;        {Number of stop bits}
  ParityEnableMask = $08;        {Parity enable}
  EvenParityMask   = $10;        {Even parity select}
  StickParityMask  = $20;        {Stick parity select}
  SetBreakMask     = $40;        {Set break}
  DLABMask         = $80;        {Set divisor latch access}

  {Line status bit masks}
  DataReadyMask    = $01;        {Receive char is ready}
  OverrunErrorMask = $02;        {Overrun error received}
  ParityErrorMask  = $04;        {Parity error received}
  FramingErrorMask = $08;        {Framing error received}
  BreakReceivedMask = $10;        {Break received}
  THREMask         = $20;        {Transmitter holding register is empty}
  TEMask           = $40;        {Transmitter is empty}
  FIFOErrorMask    = $80;        {FIFO error received}

  {Modem status bit masks}
  DeltaCTSMask     = $01;        {CTS changed since last read}
  DeltaDSRMask     = $02;        {DSR changed since last read}
  DeltaRIMask      = $04;        {RI changed since last read}
  DeltaDCDMask     = $08;        {DCD changed since last read}
  CTSMask          = $10;        {Clear to send}
  DSRMask          = $20;        {Data set ready}
  RIMask           = $40;        {Ring indicator}
  DCDMask          = $80;        {Data carrier detect}

  {Interrupt enable bit masks}
  ReceiveIntMask   = $01;        {Interrupt on received data}
  TransmitIntMask  = $02;        {Interrupt on THR empty}
  LineIntMask      = $04;        {Interrupt on line status change}
  ModemIntMask     = $08;        {Interrupt on modem status change}

  {Hardware flow control options}
  hfUseDTR         = $01;   {Use DTR for receive flow control}
  hfUseRTS         = $02;   {Use RTS for receive flow control}
  hfRequireDSR     = $04;   {Require DSR before transmittting}
  hfRequireCTS     = $08;   {Require CTS before transmittting}
  hfDTRActiveLow   = $10;   {Make DTR active low}
  hfRTSActiveLow   = $20;   {Make RTS active low}
  hfDSRActiveLow   = $40;   {Make DSR active low}
  hfCTSActiveLow   = $80;   {Make CTS active low}

  sfReceiveFlow    = $01;   {Use receiver flow control}
  sfTransmitFlow   = $02;   {User transmitter flow control}
  DefSWFOpt        = sfReceiveFlow + sfTransmitFlow;

  {The following table is used internally only. It is interfaced so
  that other programmers can use them with their own CRC routines}

  CrcTable: Array [0..255] Of Word = (
  $0000,  $1021,  $2042,  $3063,  $4084,  $50a5,  $60c6,  $70e7,
  $8108,  $9129,  $a14a,  $b16b,  $c18c,  $D1ad,  $e1ce,  $f1ef,
  $1231,  $0210,  $3273,  $2252,  $52b5,  $4294,  $72f7,  $62D6,
  $9339,  $8318,  $b37b,  $a35a,  $D3bd,  $c39c,  $f3ff,  $e3de,
  $2462,  $3443,  $0420,  $1401,  $64e6,  $74c7,  $44a4,  $5485,
  $a56a,  $b54b,  $8528,  $9509,  $e5ee,  $f5cf,  $c5ac,  $D58D,
  $3653,  $2672,  $1611,  $0630,  $76D7,  $66f6,  $5695,  $46b4,
  $b75b,  $a77a,  $9719,  $8738,  $f7df,  $e7fe,  $D79D,  $c7bc,
  $48c4,  $58e5,  $6886,  $78a7,  $0840,  $1861,  $2802,  $3823,
  $c9cc,  $D9ed,  $e98e,  $f9af,  $8948,  $9969,  $a90a,  $b92b,
  $5af5,  $4ad4,  $7ab7,  $6a96,  $1a71,  $0a50,  $3a33,  $2a12,
  $dbfd,  $cbdc,  $fbbf,  $eb9e,  $9b79,  $8b58,  $bb3b,  $ab1a,
  $6ca6,  $7c87,  $4ce4,  $5cc5,  $2c22,  $3c03,  $0c60,  $1c41,
  $edae,  $fd8f,  $cdec,  $ddcd,  $ad2a,  $bd0b,  $8D68,  $9D49,
  $7e97,  $6eb6,  $5ed5,  $4ef4,  $3e13,  $2e32,  $1e51,  $0e70,
  $ff9f,  $efbe,  $dfdd,  $cffc,  $bf1b,  $af3a,  $9f59,  $8f78,
  $9188,  $81a9,  $b1ca,  $a1eb,  $D10c,  $c12D,  $f14e,  $e16f,
  $1080,  $00a1,  $30c2,  $20e3,  $5004,  $4025,  $7046,  $6067,
  $83b9,  $9398,  $a3fb,  $b3da,  $c33D,  $D31c,  $e37f,  $f35e,
  $02b1,  $1290,  $22f3,  $32D2,  $4235,  $5214,  $6277,  $7256,
  $b5ea,  $a5cb,  $95a8,  $8589,  $f56e,  $e54f,  $D52c,  $c50D,
  $34e2,  $24c3,  $14a0,  $0481,  $7466,  $6447,  $5424,  $4405,
  $a7db,  $b7fa,  $8799,  $97b8,  $e75f,  $f77e,  $c71D,  $D73c,
  $26D3,  $36f2,  $0691,  $16b0,  $6657,  $7676,  $4615,  $5634,
  $D94c,  $c96D,  $f90e,  $e92f,  $99c8,  $89e9,  $b98a,  $a9ab,
  $5844,  $4865,  $7806,  $6827,  $18c0,  $08e1,  $3882,  $28a3,
  $cb7D,  $db5c,  $eb3f,  $fb1e,  $8bf9,  $9bd8,  $abbb,  $bb9a,
  $4a75,  $5a54,  $6a37,  $7a16,  $0af1,  $1ad0,  $2ab3,  $3a92,
  $fd2e,  $ed0f,  $dd6c,  $cd4D,  $bdaa,  $ad8b,  $9de8,  $8dc9,
  $7c26,  $6c07,  $5c64,  $4c45,  $3ca2,  $2c83,  $1ce0,  $0cc1,
  $ef1f,  $ff3e,  $cf5D,  $df7c,  $af9b,  $bfba,  $8fd9,  $9ff8,
  $6e17,  $7e36,  $4e55,  $5e74,  $2e93,  $3eb2,  $0ed1,  $1ef0
  );

  AproFileMode : Byte = $40; {readonly filemode}
  PS2DetectMode : PS2Mode = PS2Auto;

  {Convenient character constants (and aliases)}
  cNul = #0;
  cSoh = #1;
  cStx = #2;
  cEtx = #3;
  cEot = #4;
  cEnq = #5;
  cAck = #6;
  cBel = #7;
  cBS  = #8;
  cTab = #9;
  cLF  = #10;
  cVT  = #11;
  cFF  = #12;
  cCR  = #13;
  cSO  = #14;
  cSI  = #15;
  cDle = #16;
  cDC1 = #17;       cXon  = #17;
  cDC2 = #18;
  cDC3 = #19;       cXoff = #19;
  cDC4 = #20;
  cNak = #21;
  cSyn = #22;
  cEtb = #23;
  cCan = #24;
  cEM  = #25;
  cSub = #26;
  cEsc = #27;
  cFS  = #28;
  cGS  = #29;
  cRS  = #30;
  cUS  = #31;

  DefaultXonChar : Char = cXon;    {Standard Xon char (DC1)}
  DefaultXoffChar : Char = cXoff;  {Standard Xoff char (DC3)}

  {---- Option codes for ports ----}
  ptReturnPartialGets  = $0001;   {True to return partial strings}
  ptReturnDelimiter    = $0002;   {True to return delim char}
  ptExecutePartialPuts = $0004;   {True to send partial blocks}
  ptIgnoreDelimCase    = $0008;   {True to ignore case on DelimSets}
  ptRestoreOnClose     = $0010;   {True to restore UART on close}
  ptDropModemOnClose   = $0020;   {True to drop modem signals on close}
  ptRaiseModemOnOpen   = $0040;   {True to raise modem signals on open}
  ptBufferGetChar      = $1000;   {Set to use buffered reads}
  ptHandleFossilBug    = $8000;   {Set to handle OutBuffUsed bug in FOSSIL driver}
  ptTrueOutBuffFree    = $4000;   {Set to return true OutBuffFree value}
  ptPutCharWait        = $2000;   {Set to use "wait" transmit}

  {---- Internal option codes for ports ----}
  ptHiIrq              = $1000;   {True if IRQ > 7}

  DefPortOptionsSimple = ptReturnPartialGets +
  ptReturnDelimiter +
  ptExecutePartialPuts +
  ptDropModemOnClose +
  ptRaiseModemOnOpen +
  ptRestoreOnClose;
  DefPortOptions : Word = DefPortOptionsSimple;
  BadPortOptions : Word = ptHiIrq;

  MinInBuff = 10;            {Min allowable input buffer size}
  MinOutBuff = 10;           {Min allowable output buffer size}

  DefaultLineOptions : LineOptionRecord =
  (Parity : NoParity;
  DataBits : 8;
  StopBits : 1;
  Options : DefPortOptionsSimple;
  InSize : 2048;
  OutSize : 2048 + 30);

  DefFossilOptions : Word = ptHandleFossilBug + ptPutCharWait;

  {Modem control bit masks}
  DTRMask          = $01;        {Data terminal ready}
  RTSMask          = $02;        {Request to send}
  Out1Mask         = $04;        {Output bit 1}
  Out2Mask         = $08;        {Output bit 2}
  LoopbackMask     = $10;        {Loopback testing}

  {Error/status code constants}
  ecOk                     = 0;     {Reset value for AsyncStatus}

  BadDate  = $FFFF;
  MinYear  = 1900;
  MaxYear  = 2078;
  First2Months = 58;         {1900 was not a leap year}
  FirstDayOfWeek = Monday;   {01/01/1900 was a Monday}
  BadTime = $FFFFFFFF;
  SecondsInHour = 3600;      {number of seconds in an hour}
  SecondsInMinute = 60;      {number of seconds in a minute}
  HoursInDay = 24;           {number of hours in a day}
  SecondsInDay = 86400;      {number of seconds in a day}

  {+++General error codes (0-999)+++}

  {DOS errors}
  ecFileNotFound           = 0002;  {File not found}
  ecPathNotFound           = 0003;  {Path not found}
  ecTooManyFiles           = 0004;  {Too many open files}
  ecAccessDenied           = 0005;  {File access denied}
  ecInvalidHandle          = 0006;  {Invalid file handle}
  ecOutOfMemory            = 0008;  {Insufficient memory}
  ecInvalidDrive           = 0015;  {Invalid drive}
  ecNoMoreFiles            = 0018;  {No more files}

  {Turbo Pascal I/O errors}
  ecDiskRead               = 0100;  {Attempt to read beyond end of file}
  ecDiskFull               = 0101;  {Disk is full}
  ecNotAssigned            = 0102;  {File not Assign-ed}
  ecNotOpen                = 0103;  {File not open}
  ecNotOpenInput           = 0104;  {File not open for input}
  ecNotOpenOutput          = 0105;  {File not open for output}
  ecInvalidFormat          = 0106;  {Invalid format for packed window}

  {DOS critical errors}
  ecWriteProtected         = 0150;  {Disk is write-protected}
  ecUnknownUnit            = 0151;  {Unknown disk unit}
  ecDriveNotReady          = 0152;  {Drive is not ready}
  ecUnknownCommand         = 0153;  {Unknown command}
  ecCrcError               = 0154;  {Data error}
  ecBadStructLen           = 0155;  {Bad request structure length}
  ecSeekError              = 0156;  {Seek error}
  ecUnknownMedia           = 0157;  {Unknown media type}
  ecSectorNotFound         = 0158;  {Disk sector not found}
  ecOutOfPaper             = 0159;  {Printer is out of paper}
  ecDeviceWrite            = 0160;  {Device write error}
  ecDeviceRead             = 0161;  {Device read error}
  ecHardwareFailure        = 0162;  {General failure}

  {+++Capacity or environmental errors (2900-2999)+++}

  {APUART port errors}
  ecNoMorePorts            = 2900;  {Can't open port, no slots available}
  ecOverrunError           = 2901;  {UART receiver overrun}
  ecParityError            = 2902;  {UART receiver parity error}
  ecFramingError           = 2903;  {UART receiver framing error}

  {APINT14 port errors}
  ecTransmitFailed         = 2910;  {Int14 transmit failed}
  ecUartError              = 2911;  {Int14 receive failed}

  {APCOM/OOCOM errors/status}
  ecBlockIncomplete        = 2920;  {Block shorter than requested}
  ecBufferIsFull           = 2921;  {No room for new char in buffer}
  ecBufferIsEmpty          = 2922;  {No characters to get}
  ecTimeout                = 2923;  {Timed out waiting for something}
  ecStringIncomplete       = 2924;  {String shorter than requested}
  ecStringOverrun          = 2925;  {String longer than 255}
  ecUserAbort              = 2926;  {User aborted during "wait"}

  {APMODEM/OOMODEM errors}
  ecTableFull              = 2930;  {No room in table to add new entry}
  ecNullCommand            = 2931;  {Modem - no command registered}

  {Tracing/EventFile file errors}
  ecEventFileError         = 2940;  {Failed to open or write to the event file}
  ecTraceFileError         = 2941;  {Failed to open or write to the trace file}

  {Constants for supported device types}
  NoDevice = 0;
  UartDevice = 1;
  Int14Device = 2;
  FossilDevice = 3;
  Digi14Device = 4;

  {Other device layer errors}
  ecNoFossil               = 2950;  {No fossil installed}
  ecDigiFailure            = 2960;  {Generic Digiboard failure code}

  {+++Warnings or user errors (7900-7999)+++}
  {This category not currently used by Async Professional}

  {+++Programmer errors (8900-8999)+++}

  {APCOM/OOCOM port errors}
  ecBadPortNumber          = 8900;  {Out-of-range port number}
  ecOutofRange             = 8901;  {General out-of-range error}
  ecPortNotOpen            = 8902;  {Port not open}
  ecInvalidBaudRate        = 8903;  {Bad baud rate for this device}
  ecInvalidArgument        = 8904;  {General programming error}
  ecNoDevice               = 8905;  {No device layer installed}
  ecNotaUart               = 8906;  {Couldn't find a uart at this address}
  ecInvalidParity          = 8907;  {Bad parity option for this device}
  ecNotBuffered            = 8910;  {Operation only allowed on buffered ports}
  ecNotSupported           = 8911;  {Function not supported by device-layer}

  {+++Status codes (9700-9999)+++}

  {APABSPCL/OOABSPCL status codes}
  ecInitFail               = 9900;  {Xmodem init failed}
  ecInitCancel             = 9901;  {Xmodem init was canceled on request}
  ecCancelRequested        = 9902;  {Cancel requested}
  ecDuplicateBlock         = 9903;  {Duplicate block received}
  ecSequenceError          = 9904;  {Wrong block number received}
  ecDirNotFound            = 9905;  {Directory not found in protocol transmit}
  ecNoMatchingFiles        = 9906;  {No matching files in protocol transmit}
  ecLongPacket             = 9907;  {Long packet received during protocol}
  ecEndFile                = 9908;  {End of transmitted file}
  ecHandshakeInProgress    = 9909;  {Initial protocol handshake in progress}
  ecFileRenamed            = 9910;  {Incoming file was renamed}
  ecFileAlreadyExists      = 9911;  {Incoming file already exists}
  ecInvalidFilesize        = 9912;  {Ymodem header has bad file size}
  ecInvalidDateTime        = 9913;  {Ymodem header has bad date/time}
  ecUnexpectedChar         = 9914;  {Unexpected char during protocol}
  ecBlockCheckError        = 9915;  {Incorrect CRC or checksum received}
  ecNoSearchMask           = 9916;  {No search mask specified for transmit}
  ecNoFilename             = 9917;  {No filename specifed in xmodem download}
  ecAsciiReceiveInProgress = 9918;  {Ascii receive in progress}
  ecFileRejected           = 9919;  {Receiver rejected file}
  ecTooManyErrors          = 9920;  {Too many errors received during protocol}
  ecBadFileList            = 9921;  {No end of list marker found in file list}

  {APZMODEM/OOZMODEM status codes}
  ecGotCrcE                = 9925;  {Zmodem - got CrcE DataSubpacket}
  ecGotCrcW                = 9926;  {Zmodem - got CrcW DataSubpacket}
  ecGotCrcQ                = 9927;  {Zmodem - got CrcQ DataSubpacket}
  ecGotCrcG                = 9928;  {Zmodem - got CrcG DataSubpacket}
  ecGarbage                = 9929;  {Zmodem - got garbage from remote}
  ecSkipFile               = 9930;  {Zmodem - skip file}
  ecBadPosition            = 9931;  {Zmodem - bad file position}
  ecFileDoesntExist        = 9932;  {Zmodem - specified file doesn't exist}
  ecCantWriteFile          = 9933;  {Zmodem - not allowed to overwrite file}
  ecFailedToHandshake      = 9934;  {Zmodem - never got proper handshake}
  ecNoFilesToReceive       = 9935;  {Zmodem - no files to receive}
  ecBuffersTooSmall        = 9936;  {ZModem - port buffers too small}
  ecGotHeader              = 9937;  {Zmodem - got a complete header}
  ecNoHeader               = 9938;  {Zmodem - (internal) no header yet}

  {APMODEM/OOMODEM status codes}
  ecUnknownModemResult     = 9940;  {Unexpected char in modem result string}
  ecConnect                = 9941;  {Modem response - CONNECT}
  ecRing                   = 9942;  {Modem response - RING}
  ecNoCarrier              = 9943;  {Modem response - NO CARRIER}
  ecNoDialTone             = 9944;  {Modem response - NO DIALTONE}
  ecBusy                   = 9945;  {Modem response - BUSY}
  ecNoAnswer               = 9947;  {Modem response - NO ANSWER}
  ecRinging                = 9948;  {Modem response - RINGING}
  ecVoice                  = 9949;  {Modem response - VOICE}
  ecError                  = 9950;  {Modem response - ERROR}

  {APMODEM2/OOMODEM2 status codes}
  ecTimeUpd                = 9951;  {Message reporting time remaining}
  ecGotBaud                = 9952;  {Received the baud rate}
  ecGotErrorCorrection     = 9953;  {Received an error correction tag}
  ecGotDataCompression     = 9954;  {Received a data compression tag}
  ecModemBusy              = 9955;  {Modem processing command}
  ecModemNotBusy           = 9956;  {Modem not doing anything}

  {APKERMIT/OOKERMIT status codes}
  ecGotData                = 9954;  {Kermit - got packet}
  ecNoData                 = 9955;  {Kermit - no data yet}

  {Archive status messages}
  ecUnknownMethod          = 9960;  {Unknown compression method}
  ecFileEncrypted          = 9961;  {Cannot extract--file is encrypted}
  ecBadFileCRC             = 9962;  {Bad CRC--file is probably corrupted}
  ecCannotCreate           = 9963;  {Unable to create output file}
  ecBadFileFormat          = 9964;  {Bad archive file format}
  ecNotAnLzhFile           = 9965;  {Not an LZH file}
  ecNotAZipFile            = 9966;  {Not a ZIP file}
  ecEmptyFileMaskList      = 9967;  {File mask list is empty}

  {CaptureTerminalWindow error codes}
  ecScrollBackTooBig       = 9980;  {Scroll back buffer > 64k}

  {APBPLUS/OOBPLUS status codes}
  ecResync                 = 9985;  {Resyncing with host}
  ecWaitACK                = 9986;  {Waiting for ACK}
  ecDropout                = 9987;  {Dropout}
  ecHostCan                = 9988;  {Host cancel}
  ecTryResume              = 9989;  {Attempting resume}
  ecHostResume             = 9990;  {Host resuming}
  ecResumeOK               = 9991;  {Resumed OK}
  ecResumeBad              = 9992;  {Failed to resume}
  ecUnPacket               = 9993;  {Invalid packet type}

  {Error types}
  etFatal          = 0;          {Fatal errors}
  etNonFatal       = 1;          {Non-fatal I/O errors}
  etWarning        = 2;          {Warning messages (currently not used)}
  etMessage        = 3;          {Status information (generally should
  not be acted on by an error handler)}

  {Error prefixes}
  epFatal          = etFatal * 10000;
  epNonFatal       = etNonFatal * 10000;
  epWarning        = etWarning * 10000;
  epMessage        = etMessage * 10000;

  Threshold2000 : Integer = 1900;
  MaxActivePort = 36;

Var
  ActiveComPort : Array [1..MaxActivePort] Of PortRecPtr;
  Crc32TableOfs    : Word;
  InitPort         : InitPortProc;
  InitPortKeep     : InitPortKeepProc;
  DonePort         : DonePortProc;
  SetLine          : SetLineProc;
  GetLine          : GetLineProc;
  SetModem         : SetModemProc;
  GetModem         : GetModemProc;
  GetChar          : GetCharProc;
  PeekChar         : PeekCharProc;
  PutChar          : PutCharProc;
  StartTransmitter : StartTransmitterProc;
  CharReady        : CharReadyFunc;
  TransReady       : TransReadyFunc;
  SendBreak        : SendBreakProc;
  ActivatePort     : ActivatePortProc;
  DeactivatePort   : ActivatePortProc;
  SavePort         : SavePortProc;
  RestorePort      : SavePortProc;
  GotError         : GotErrorProc;

  {Procedure pointers needed by alternate device layers}
  UpdateLineStatus  : UpdateLineStatusFunc;
  UpdateModemStatus : UpdateModemStatusFunc;
  HWFlowSet         : FlowSetProc;
  HWFlowGet         : FlowGetFunc;
  SWFlowSet         : FlowSetProc;
  SWFlowGet         : FlowGetFunc;
  SWFlowCtl         : FlowCtlProc;
  BufferStatus      : BufferStatusProc;
  BufferFlush       : BufferFlushProc;
  SetUart           : SetUartProc;

Procedure SetAsyncStatus (Status : Word);
Function GetAsyncStatus : Word;
Function GetMemCheck (Var P; Bytes : Word) : Boolean;
Procedure FreeMemCheck (Var P; Bytes : Word);
Function GetAproFileMode : Byte;
Procedure SetAproFileMode (NewMode : Byte);

Function DMYtoDate (Day, Month, Year : Integer) : Date;
Procedure DateToDMY (Julian : Date; Var Day, Month, Year : Integer);
Procedure TimeToHMS (T : Time; Var Hours, Minutes, Seconds : Byte);
Function HMStoTime (Hours, Minutes, Seconds : Byte) : Time;
Function ValidDate (Day, Month, Year : Integer) : Boolean;
Function DaysInMonth (Month, Year : Integer) : Integer;
Function IsLeapYear (Year : Integer) : Boolean;
Procedure IncDateTime (Var DT1, DT2 : DateTimeRec; Days : Integer; Secs : LongInt);
Procedure DateTimeDiff (DT1, DT2 : DateTimeRec; Var Days : Word; Var Secs : LongInt);
Function NoAbortFunc : Boolean;
Procedure NoErrorProc (P : Pointer; Var StatusCode : Word);

Function UpdateCrc (CurByte : Byte; CurCrc : Word) : Word;
Function UpdateChecksum (CurByte : Byte; CheckSum : Word) : Word;
Function UpdateCrcKermit (CurByte : Byte; CurCrc : Word) : Word;
Function CheckRange (Value, Low, High : Word) : Boolean;

Implementation

Function UpdateChecksum (CurByte : Byte; CheckSum : Word) : Word;
  {-Returns an updated checksum}
Begin
  UpdateCheckSum := CheckSum + CurByte;
End;

Function UpdateCrcKermit (CurByte : Byte; CurCrc : Word) : Word;
  {-Returns an updated Crc16 (kermit style)}
Var
  I : Integer;
  Temp : Integer;
Begin
  For I := 0 To 7 Do Begin
    Temp := CurCrc XOr CurByte;
    CurCrc := CurCrc ShR 1;
    If Odd (Temp) Then
      CurCrc := CurCrc XOr $8408;
    CurByte := CurByte ShR 1;
  End;
  UpdateCrcKermit := CurCrc;
End;

Function UpdateCrc (CurByte : Byte; CurCrc : Word) : Word;
  {-Returns an updated CRC16}
Begin
  UpdateCrc := CrcTable [ ( (CurCrc ShR 8) And 255) ] XOr
  (CurCrc ShL 8) XOr CurByte;
End;

Procedure NoErrorProc (P : Pointer; Var StatusCode : Word);
  {-Dummy error procedure}
Begin
End;

Function NoAbortFunc : Boolean;
  {-Empty abort function}
Begin
  NoAbortFunc := False;
End;

Procedure DateTimeDiff (DT1, DT2 : DateTimeRec; Var Days : Word; Var Secs : LongInt);
  {-Return the difference in days and seconds between two points in time}
Var
  DTTemp : DateTimeRec;
Begin
  {swap if DT1 later than DT2}
  If (DT1.D > DT2.D) Or ( (DT1.D = DT2.D) And (DT1.T > DT2.T) ) Then Begin
    DTTemp := DT1;
    DT1 := DT2;
    DT2 := DTTemp;
  End;

  {the difference in days is easy}
  Days := DT2.D - DT1.D;

  {difference in seconds}
  If DT2.T < DT1.T Then Begin
    {subtract one day, add 24 hours}
    Dec (Days);
    Inc (DT2.T, SecondsInDay);
  End;
  Secs := DT2.T - DT1.T;
End;

Procedure IncDateTime (Var DT1, DT2 : DateTimeRec; Days : Integer; Secs : LongInt);
  {-Increment (or decrement) DT1 by the specified number of days and seconds
    and put the result in DT2}
Begin
  DT2 := DT1;

  {date first}
  Inc (Integer (DT2.D), Days);

  If Secs < 0 Then Begin
    {change the sign}
    Secs := - Secs;

    {adjust the date}
    Dec (DT2.D, Secs Div SecondsInDay);
    Secs := Secs Mod SecondsInDay;

    If Secs > DT2.T Then Begin
      {subtract a day from DT2.D and add a day's worth of seconds to DT2.T}
      Dec (DT2.D);
      Inc (DT2.T, SecondsInDay);
    End;

    {now subtract the seconds}
    Dec (DT2.T, Secs);
  End
  Else Begin
    {increment the seconds}
    Inc (DT2.T, Secs);

    {adjust date if necessary}
    Inc (DT2.D, DT2.T Div SecondsInDay);

    {force time to 0..SecondsInDay-1 range}
    DT2.T := DT2.T Mod SecondsInDay;
  End;
End;

Function IsLeapYear (Year : Integer) : Boolean;
  {-Return True if Year is a leap year}
Begin
  IsLeapYear := (Year Mod 4 = 0) And (Year Mod 4000 <> 0) And
                ((Year Mod 100 <> 0) Or (Year Mod 400 = 0));
End;

Function GetAproFileMode : Byte;
Begin
  GetAproFileMode := AproFileMode;
End;

Procedure SetAproFileMode (NewMode : Byte);
Begin
  AproFileMode := NewMode;
End;

Function GetMemCheck (Var P; Bytes : Word) : Boolean;
Var
  Pt : Pointer Absolute P;
Begin
  GetMem (Pt, Bytes);
  GetMemCheck := (Pt <> Nil);
End;

Procedure FreeMemCheck (Var P; Bytes : Word);
  {-Deallocate heap space}
Var
  Pt : Pointer Absolute P;
Begin
  If Pt <> Nil Then Begin
    FreeMem (Pt, Bytes);
    Pt := Nil;
  End;
End;

Function GetAsyncStatus : Word;
Begin
  GetAsyncStatus := AsyncStatus;
End;

Procedure SetAsyncStatus (Status : Word);
Begin
  AsyncStatus := Status;
End;

Function DMYtoDate (Day, Month, Year : Integer) : Date;
  {-Convert from day, month, year to a julian date}
Begin
  If Word (Year) < 100 Then Begin
    Inc (Year, 1900);
    If Year < Threshold2000 Then
      Inc (Year, 100);
  End;

  If Not ValidDate (Day, Month, Year) Then
    DMYtoDate := BadDate
  Else If (Year = MinYear) And (Month < 3) Then
    If Month = 1 Then
      DMYtoDate := Pred (Day)
    Else
      DMYtoDate := Day + 30
  Else Begin
    If Month > 2 Then
      Dec (Month, 3)
    Else Begin
      Inc (Month, 9);
      Dec (Year);
    End;
    Dec (Year, MinYear);
    DMYtoDate :=
    ( (LongInt (Year) * 1461) Div 4) +
    ( ( (153 * Month) + 2) Div 5) + Day + First2Months;
  End;
End;

Procedure DateToDMY (Julian : Date; Var Day, Month, Year : Integer);
  {-Convert from a julian date to month, day, year}
Var
  I : LongInt;
Begin
  If Julian = BadDate Then Begin
    Day := 0;
    Month := 0;
    Year := 0;
  End
  Else If Julian <= First2Months Then Begin
    Year := MinYear;
    If Julian <= 30 Then Begin
      Month := 1;
      Day := Succ (Julian);
    End
    Else Begin
      Month := 2;
      Day := Julian - 30;
    End;
  End
  Else Begin
    I := (4 * LongInt (Julian - First2Months) ) - 1;
    Year := I Div 1461;
    I := (5 * ( (I Mod 1461) Div 4) ) + 2;
    Month := I Div 153;
    Day := ( (I Mod 153) + 5) Div 5;
    If Month < 10 Then
      Inc (Month, 3)
    Else Begin
      Dec (Month, 9);
      Inc (Year);
    End;
    Inc (Year, MinYear);
  End;
End;

Procedure TimeToHMS (T : Time; Var Hours, Minutes, Seconds : Byte);
  {-Convert a Time variable to Hours, Minutes, Seconds}
Begin
  If T = BadTime Then Begin
    Hours := 0;
    Minutes := 0;
    Seconds := 0;
  End
  Else Begin
    Hours := T Div SecondsInHour;
    Dec (T, LongInt (Hours) * SecondsInHour);
    Minutes := T Div SecondsInMinute;
    Dec (T, LongInt (Minutes) * SecondsInMinute);
    Seconds := T;
  End;
End;

Function HMStoTime (Hours, Minutes, Seconds : Byte) : Time;
  {-Convert Hours, Minutes, Seconds to a Time variable}
Var
  T : Time;
Begin
  Hours := Hours Mod HoursInDay;
  T := (LongInt (Hours) * SecondsInHour) + (LongInt (Minutes) * SecondsInMinute) + Seconds;
  HMStoTime := T Mod SecondsInDay;
End;

Function ValidDate (Day, Month, Year : Integer) : Boolean;
  {-Verify that day, month, year is a valid date}
Begin
  If Word (Year) < 100 Then Begin
    Inc (Year, 1900);
    If Year < Threshold2000 Then
      Inc (Year, 100);
  End;

  If (Day < 1) Or (Year < MinYear) Or (Year > MaxYear) Then
    ValidDate := False
  Else Case Month Of
    1..12 :
             ValidDate := Day <= DaysInMonth (Month, Year);
    Else
      ValidDate := False;
  End
End;

Function DaysInMonth (Month, Year : Integer) : Integer;
  {-Return the number of days in the specified month of a given year}
Begin
  If Word (Year) < 100 Then Begin
    Inc (Year, 1900);
    If Year < Threshold2000 Then
      Inc (Year, 100);
  End;

  Case Month Of
    1, 3, 5, 7, 8, 10, 12 :
                             DaysInMonth := 31;
    4, 6, 9, 11 :
                   DaysInMonth := 30;
    2 :
         DaysInMonth := 28 + Ord (IsLeapYear (Year) );
    Else
      DaysInMonth := 0;
  End;
End;

Function CheckRange (Value, Low, High : Word) : Boolean;
  {-Range check Value}
Begin
  If (Value < Low) Or (Value > High) Then Begin
    CheckRange := False;
  End Else
    CheckRange := True;
End;

End.