[Back to DOS SWAG index] [Back to Main SWAG index] [Original]

    {
    I'm very glad to be useful and to post the enhanced DOS unit for Turbo Pascal
    7.0. It includes lots of nice routines written on inline asm, combined with
    short comments and explanations. All you have in standard DOS unit you may
    find in EnhDOS as well except of Exec and SwapVectors. Sure, the full source
    code!

    What is good?
    -----------------

    1. Fast! (because of the asm)
    2. Flexible! (less procedures, more functions, lots of parameters)
    3. Good error-handling routines. (don't need to care to check errors at all)
    4. _Strong_ file service. (lots of file functions)
    5. Lots of additional DOS service functions that can't be found in any standard
       or non-standard Pascal, C,... library.
    6. Windows (tm) compatible (means you may use these routines when developing
       Windows (tm) applications.
    7. Own memory allocate/release routines. (used DOS memory allocation)
    8. Free. Released to a Public Domain.

    What is bad?
    -----------------

    1. Requires Borland Turbo Pascal version 7.0 or later (7.01)
    2. Requires DOS 3.1 or later. Sorry guys, wanna cool service - need later DOS.
    3. Won't run on XT personal computers. (uses 286 instructions)
    4. No more strings. (all string-type names are of PChar type)
    5. Exec and SwapVectors not implemented. If you'd like this code, I will
       continue modifying this unit and will eventually add the above functions
       too.

    Well, routines were checked on IBM PS/2 386SX, seems like work fine!

    Greetingz to
    -----------------

     Bas van Gaalen (cool asm programmer and my PASCAL area friend ;)
     Dj Murdoch (best explainer ;)
     Gayle Davis (SWAG live forever) Feel free to place it into a next SWAG bundle.
     Ralph Brown (brilliant idea to make the interrupt list)
     Alex Grischenko (whose asm help was very appreciated)
     ...and all of you, guys!

    Material used
    -----------------

    Borland Pascal 7.0 Runtime Library source code
    Ralph Brown's Interrupt List
    Tech Help 4.0

    You may use this source-code-software in ANY purpose. Code may be changed.
    If some of the routines won't work, please send me a message.
    If you don't mind, please leave my copyright strings as they are.}

    Unit EnhDOS;
    (*
      Turbo Pascal 7.0 - ENHDOS.PAS

      Enhanced DOS interface unit for DOS 3.1+ ***  Version 1.1  April, 1994.
      Copyright (c) 1994  by Andrew Eigus           Fidonet 2:5100/33

      Runtime Library Portions Copyright (c) 1991,92 Borland International }

      THIS UNIT SOURCE IS FREE
    *)

    interface

    {$X+} { Enable extended syntax }
    {$G+} { Enable 286+ instructions }

    const

      { My copyright information }

      Copyright : PChar = 'Portions Copyright (c) 1994 by Andrew Eigus';

      { GetDriveType return values }

      dtError     = $00; { Bad drive }
      dtFixed     = $01; { Fixed drive }
      dtRemovable = $02; { Removable drive }
      dtRemote    = $03; { Remote (network) drive }

      { Handle file open modes (om) constants }

      omRead           = $00; { Open file for input only }
      omWrite          = $01; { Open file for output only }
      omReadWrite      = $02; { Open file for input or/and output (both modes) }
      omShareCompat    = $00; { Modes used when SHARE.EXE loaded }
      omShareExclusive = $10;
      omShareDenyWrite = $20;
      omShareDenyRead  = $30;
      omShareDenyNone  = $40;

      { Maximum file name component string lengths }

      fsPathName       = 79;
      fsDirectory      = 67;
      fsFileSpec       = 12;
      fsFileName       = 8;
      fsExtension      = 4;

      { FileSplit return flags }

      fcExtension      = $0001;
      fcFileName       = $0002;
      fcDirectory      = $0004;
      fcWildcards      = $0008;

      { File attributes (fa) constants }

      faNormal         = $00;
      faReadOnly       = $01;
      faHidden         = $02;
      faSysFile        = $04;
      faVolumeID       = $08;
      faDirectory      = $10;
      faArchive        = $20;
      faAnyFile        = $3F;

      { Seek start offset (sk) constants }

      skStart = 0; { Seek position relative to the beginning of a file }
      skPos   = 1; { Seek position relative to a current file position }
      skEnd   = 2; { Seek position relative to the end of a file }

      { Error handler function (fr) result codes }

      frOk    = 0; { Continue program }
      frRetry = 1; { Retry function once again }

      { Function codes (only passed to error handler routine) (fn) constants }

      fnGetDPB         = $3200;
      fnGetDiskSize    = $3600;
      fnGetDiskFree    = $3601;
      fnGetCountryInfo = $3800;
      fnSetDate        = $2B00;
      fnSetTime        = $2D00;
      fnIsFixedDisk    = $4408;
      fnIsNetworkDrive = $4409;
      fnCreateDir      = $3900;
      fnRemoveDir      = $3A00;
      fnGetCurDir      = $4700;
      fnSetCurDir      = $3B00;
      fnDeleteFile     = $4100;
      fnRenameFile     = $5600;
      fnGetFileAttr    = $4300;
      fnSetFileAttr    = $4301;
      fnFindFirst      = $4E00;
      fnFindNext       = $4F00;
      fnCreateFile     = $5B00;
      fnCreateTempFile = $5A00;
      fnOpenFile       = $3D00;
      fnRead           = $3F00;
      fnWrite          = $4000;
      fnSeek           = $4200;
      fnGetFDateTime   = $5700;
      fnSetFDateTime   = $5701;
      fnCloseFile      = $3E00;
      fnMemAlloc       = $4800;
      fnMemFree        = $4900;

      { DOS 3.x+ errors/return codes }

      dosrOk                = 0;   { Success }
      dosrInvalidFuncNumber = 1;   { Invalid DOS function number }
      dosrFileNotFound      = 2;   { File not found }
      dosrPathNotFound      = 3;   { Path not found }
      dosrTooManyOpenFiles  = 4;   { Too many open files }
      dosrFileAccessDenied  = 5;   { File access denied }
      dosrInvalidFileHandle = 6;   { Invalid file handle }
      dosrNotEnoughMemory   = 8;   { Not enough memory }
      dosrInvalidEnvment    = 10;  { Invalid environment }
      dosrInvalidFormat     = 11;  { Invalid format }
      dosrInvalidAccessCode = 12;  { Invalid file access code }
      dosrInvalidDrive      = 15;  { Invalid drive number }
      dosrCantRemoveDir     = 16;  { Cannot remove current directory }
      dosrCantRenameDrives  = 17;  { Cannot rename across drives }
      dosrNoMoreFiles       = 18;  { No more files }

    type

      TPathStr = array[0..fsPathName] of Char;
      TDirStr  = array[0..fsDirectory] of Char;
      TNameStr = array[0..fsFileName] of Char;
      TExtStr  = array[0..fsExtension] of Char;
      TFileStr = array[0..fsFileSpec] of Char;

      { Disk information block structure }

      PDiskParamBlock = ^TDiskParamBlock;
      TDiskParamBlock = record
        Drive : byte;             { Disk drive number (0=A, 1=B, 2=C...) }
        SubunitNum : byte;        { Sub-unit number from driver device header }
        SectSize : word;          { Number of bytes per sector }
        SectPerClust : byte;      { Number of sectors per cluster -1
                                    (max sector in cluster) }
        ClustToSectShft : byte;   { Cluster-to-sector shift }
        BootSize : word;          { Reserved sectors (boot secs; start of root dir}
        FATCount : byte;          { Number of FATs }
        MaxDir : word;            { Number of directory entries allowed in root }
        DataSect : word;          { Sector number of first data cluster }
        Clusters : word;          { Total number of allocation units (clusters)
                                    +2 (number of highest cluster) }
        FATSectors : byte;        { Sectors needed by first FAT }
        RootSect : word;          { Sector number of start of root directory }
        DeviceHeader : pointer;   { Address of device header }
        Media : byte;             { Media descriptor byte }
        AccessFlag : byte;        { 0 if drive has been accessed }
        NextPDB : pointer         { Address of next DPB (0FFFFh if last) }
      end;

      { Disk allocation data structure }

      PDiskAllocInfo = ^TDiskAllocInfo;
      TDiskAllocInfo = record
        FATId : byte;             { FAT Id }
        Clusters : word;          { Number of allocation units (clusters) }
        SectPerClust : byte;      { Number of sectors per cluster }
        SectSize : word           { Number of bytes per sector }
      end;

      { Country information structure }

      PCountryInfo = ^TCountryInfo;
      TCountryInfo = record
        DateFormat : word; { Date format value may be one of the following:
                             0 - Month, Day, Year     (USA)
                             1 - Day, Month, Year     (Europe)
                             2 - Year, Month, Day     (Japan) }

        CurrencySymbol : array[0..4] of Char; { Currency symbol string }
        ThousandsChar : byte; { Thousands separator character }
        reserved1 : byte;
        DecimalChar : byte;   { Decimal separator character }
        reserved2 : byte;
        DateChar : byte;      { Date separator character }
        reserved3 : byte;
        TimeChar : byte;      { Time separator character }
        reserved4 : byte;
        CurrencyFormat : byte; { Currency format:
                                 $XXX.XX
                                 XXX.XX$
                                 $ XXX.XX
                                 XXX.XX $
                                 XXX$XX }

        Digits : byte;          { Number of digits after decimal in currency }
        TimeFormat : byte;      { Time format may be one of the following:
                                  bit 0 = 0 if 12 hour clock
                                      1 if 24 hour clock }

        MapRoutine : pointer;   { Address of case map routine FAR CALL,
                                  AL - character to map to upper case [>=80h] }

        DataListChar : byte;    { Data-list separator character }
        reserved5 : byte;
        reserved6 : array[1..10] of Char
      end;

      THandle = Word; { Handle type (file handle and memory handle functions) }

      { Error handler function }

      TErrorFunc = function(ErrCode : integer; FuncCode : word) : byte;

      { Search record used by FindFirst and FindNext }

      TSearchRec = record
        Fill : array[1..21] of Byte;
        Attr : byte;
        Time : longint;
        Size : longint;
        Name : TFileStr
      end;

      { Date and time record used by PackTime and UnpackTime }

      TDateTime = record
        Year,
        Month,
        Day,
        Hour,
        Min,
        Sec : word
      end;

    var
      DOSResult : integer; { Error status variable }
      TempStr : array[0..High(String)] of Char;

    function SetErrorHandler(Handler : TErrorFunc) : pointer;
    function Pas2PChar(S : string) : PChar;

    function GetInDOSFlag : boolean;
    function GetDOSVersion : word;
    function GetSwitchChar : char;
    function SetSwitchChar(Switch : char) : byte;
    function GetCountryInfo(var Info : TCountryInfo) : integer;
    procedure GetDate(var Year : word; var Month, Day, DayOfWeek : byte);
    function SetDate(Year : word; Month, Day : byte) : boolean;
    procedure GetTime(var Hour, Minute, Second, Sec100 : byte);
    function SetTime(Hour, Minute, Second, Sec100 : byte) : boolean;
    function GetCBreak : boolean;
    function SetCBreak(Break : boolean) : boolean;
    function GetVerify : boolean;
    function SetVerify(Verify : boolean) : boolean;
    function GetArgCount : integer;
    function GetArgStr(Dest : PChar; Index : integer; MaxLen : word) : PChar;
    function GetEnvVar(VarName : PChar) : PChar;
    function GetIntVec(IntNo : byte; var Vector : pointer) : pointer;
    function SetIntVec(IntNo : byte; Vector : pointer) : pointer;

    function GetDTA : pointer;
    function GetCurDisk : byte;
    function SetCurDisk(Drive : byte) : byte;
    procedure GetDriveAllocInfo(Drive : byte; var Info : TDiskAllocInfo);
    function GetDPB(Drive : byte; var DPB : TDiskParamBlock) : integer;
    function DiskSize(Drive : byte) : longint;
    function DiskFree(Drive : byte) : longint;
    function IsFixedDisk(Drive : byte) : boolean;
    function IsNetworkDrive(Drive : byte) : boolean;
    function GetDriveType(Drive : byte) : byte;

    function CreateDir(Dir : PChar) : integer;
    function RemoveDir(Dir : PChar) : integer;
    function GetCurDir(Drive : byte; Dir : PChar) : integer;
    function SetCurDir(Dir : PChar) : integer;

    function DeleteFile(Path : PChar) : integer;
    function RenameFile(OldPath, NewPath : PChar) : integer;
    function ExistsFile(Path : PChar) : boolean;
    function GetFileAttr(Path : PChar) : integer;
    function SetFileAttr(Path : PChar; Attr : word) : integer;
    function FindFirst(Path : PChar; Attr: word; var F : TSearchRec) : integer;
    function FindNext(var F : TSearchRec) : integer;
    procedure UnpackTime(P : longint; var T : TDateTime);
    function PackTime(var T : TDateTime) : longint;

    function h_CreateFile(Path : PChar) : THandle;
    function h_CreateTempFile(Path : PChar) : THandle;
    function h_OpenFile(Path : PChar; Mode : byte) : THandle;
    function h_Read(Handle : THandle; var Buffer; Count : word) : word;
    function h_Write(Handle : THandle; var Buffer; Count : word) : word;
    function h_Seek(Handle : THandle; SeekPos : longint; Start : byte) : longint;
    function h_FilePos(Handle : THandle) : longint;
    function h_FileSize(Handle : THandle) : longint;
    function h_Eof(Handle : THandle) : boolean;
    function h_GetFTime(Handle : THandle) : longint;
    function h_SetFTime(Handle : THandle; DateTime : longint) : longint;
    function h_CloseFile(Handle : THandle) : integer;

    function MemAlloc(Size : longint) : pointer;
    function MemFree(P : pointer) : integer;

    function FileSearch(Dest, Name, List : PChar) : PChar;
    function FileExpand(Dest, Name : PChar) : PChar;
    function FileSplit(Path, Dir, Name, Ext : PChar) : word;

    implementation

    {$IFDEF Windows}
    {$DEFINE ProtectedMode}
    {$ENDIF}

    {$IFDEF DPMI}
    {$DEFINE ProtectedMode}
    {$ENDIF}

    {$IFDEF Windows}

    uses WinTypes, WinProcs, Strings;

    {$ELSE}

    uses Strings;

    {$ENDIF}

    const DOS = $21; { DOS interrupt number }

    var
      ErrorHandler : TErrorFunc;

    Function SetErrorHandler;
    { Sets the new error handler to hook all errors returned by EnhDOS functions,
      and returns the pointer to an old interrupt handler routine }
    Begin
      SetErrorHandler := @ErrorHandler;
      ErrorHandler := Handler
    End; { SetErrorHandler }

    Function Pas2PChar(S : string) : PChar;
    { Returns PChar type equivalent of the S variable. Use this function
      to convert strings to PChars }
    Begin
      Pas2PChar := StrPCopy(TempStr, S)
    End; { Pas2PChar }

    {$IFDEF Windows}

    procedure AnsiDosFunc; assembler;
    asm
      PUSH DS
      PUSH CX
      PUSH AX
      MOV SI,DI
      PUSH ES
      POP DS
      LEA DI,TempStr
      PUSH SS
      POP ES
      MOV CX,fsPathName
      CLD
    @@1:
      LODSB
      OR  AL,AL
      JE  @@2
      STOSB
      LOOP @@1
    @@2:
      XOR AL,AL
      STOSB
      LEA DI,TempStr
      PUSH SS
      PUSH DI
      PUSH SS
      PUSH DI
      CALL AnsiToOem
      POP AX
      POP CX
      LEA DX,TempStr
      PUSH SS
      POP DS
      INT DOS
      POP DS
    end; { AnsiDosFunc /Windows }

    {$ELSE}

    procedure AnsiDosFunc; assembler;
    asm
      PUSH DS
      MOV DX,DI
      PUSH ES
      POP DS
      INT DOS
      POP DS
    end; { AnsiDosFunc }

    {$ENDIF}

    Function GetInDOSFlag; assembler;
    { GETINDOSFLAG - DOS service function
      Description: Returns the current state of InDOS flag; fn=34h
      Returns: True if a DOS operation is being performed, False if there is
               no DOS command that currently is running }
    Asm
      MOV AH,34h
      INT DOS
      MOV AL,BYTE PTR [ES:BX]
    End; { GetInDOSFlag }

    Function GetDOSVersion; assembler;
    { GETDOSVERSION - DOS service function
      Description: Retrieves DOS version number; fn=30h
      Returns: Major DOS version number in low-order byte,
               minor version number in high-order byte of word }
    Asm
      MOV AH,30h
      INT DOS
    End; { GetDOSVersion }

    Function GetSwitchChar; assembler;
    { GETSWITCHCHAR - DOS service function
      Description: Retrieves DOS command line default switch character; fn=37h
      Returns: Switch character ('/', '-', ...) or FFh if unsupported subfunction }
    Asm
      MOV AH,37h
      XOR AL,AL
      INT DOS
      CMP AL,0FFh
      JE  @@1
      MOV AL,DL
    @@1:
    End; { GetSwitchChar }

    Function SetSwitchChar; assembler;
    { SETSWITCHCHAR - DOS service function
      Description: Sets new DOS command line switch character; fn=37h
      Returns: FFh if unsupported subfunction, any other value success }
    Asm
      MOV AX,3701h
      MOV DL,Switch
      INT DOS
    End; { SetSwitchChar }

    Function GetCountryInfo; assembler;
    { GETCOUNTRYINFO - DOS service function
      Description: Retrieves country information; fn=38h
      Returns: Country code if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      MOV AH,38h
      XOR AL,AL
      LDS DX,Info
      INT DOS
      POP DS
      JC  @@2
      MOV AX,BX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnGetCountryInfo { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { GetCountryInfo }

    Procedure GetDate; assembler;
    { GETDATE - DOS service function
      Description: Retrieves the current date set in the operating system.
                   Ranges of the values returned are: Year 1980-2099,
                   Month 1-12, Day 1-31 and DayOfWeek 0-6 (0 corresponds to
                   Sunday) }
    Asm
      MOV AH,2AH
      INT DOS
      XOR AH,AH
      LES DI,DayOfWeek
      STOSB
      MOV AL,DL
      LES DI,Day
      STOSB
      MOV AL,DH
      LES DI,Month
      STOSB
      XCHG AX,CX
      LES DI,Year
      STOSW
    End; { GetDate }

    Function SetDate; assembler;
    { SETDATE - DOS service function
      Description: Sets the current date in the operating system. Valid
                   parameter ranges are: Year 1980-2099, Month 1-12 and
                   Day 1-31
      Returns: True if the date was set, False if the date is not valid }
    Asm
      MOV CX,Year
      MOV DH,Month
      MOV DL,Day
      MOV AH,2BH
      INT DOS
      CMP AL,0
      JE  @@1
      MOV DOSResult,AX
      PUSH AX
      PUSH fnSetDate
      CALL ErrorHandler
      MOV AL,True
    @@1:
      NOT AL
    End; { SetDate }

    Procedure GetTime; assembler;
    { GETTIME - DOS service function
      Description: Returns the current time set in the operating system.
                   Ranges of the values returned are: Hour 0-23, Minute 0-59,
                   Second 0-59 and Sec100 (hundredths of seconds) 0-99 }
    Asm
      MOV AH,2CH
      INT DOS
      XOR AH,AH
      MOV AL,DL
      LES DI,Sec100
      STOSB
      MOV AL,DH
      LES DI,Second
      STOSB
      MOV AL,CL
      LES DI,Minute
      STOSB
      MOV AL,CH
      LES DI,Hour
      STOSB
    End; { GetTime }

    Function SetTime; assembler;
    { SETTIME - DOS service function
      Description: Sets the time in the operating system. Valid
                   parameter ranges are: Hour 0-23, Minute 0-59, Second 0-59 and
                   Sec100 (hundredths of seconds) 0-99
      Returns: True if the time was set, False if the time is not valid }
    Asm
      MOV CH,Hour
      MOV CL,Minute
      MOV DH,Second
      MOV DL,Sec100
      MOV AH,2DH
      INT DOS
      CMP AL,0
      JE  @@1
      MOV DOSResult,AX
      PUSH AX
      PUSH fnSetTime
      CALL ErrorHandler
      MOV AL,True
    @@1:
      NOT AL
    End; { SetTime }

    Function GetCBreak; assembler;
    { GETCBREAK - DOS service function
      Description: Retrieves Control-Break state; fn=3300h
      Returns: Current Ctrl-Break state }
    Asm
      MOV AX,3300h
      INT DOS
      MOV AL,DL
    End; { GetCBreak }

    Function SetCBreak; assembler;
    { SETCBREAK - DOS service function
      Description: Sets new Control-Break state; fn=3300h
      Returns: Old Ctrl-Break state }
    Asm
      CALL GetCBreak
      PUSH AX
      MOV AX,3301h
      MOV DL,Break
      INT DOS
      POP AX
    End; { SetCBreak }

    Function GetVerify; assembler;
    { GETVERIFY - DOS service function
      Description: Returns the state of the verify flag in DOS.
                   When off (False), disk writes are not verified.
                   When on (True), all disk writes are verified to insure proper
                   writing; fn=54h
      Returns: State of the verify flag }
    Asm
      MOV AH,54H
      INT DOS
    End; { GetVerify }

    Function SetVerify; assembler;
    { SETVERIFY - DOS service function
      Description: Sets the state of the verify flag in DOS; fn=2Eh
      Returns: Previous state of the verify flag }
    Asm
      CALL GetVerify
      PUSH AX
      MOV AL,Verify
      MOV AH,2EH
      INT DOS
      POP AX
    End; { SetVerify }

    {$IFDEF Windows}

    Procedure ArgStrCount; assembler;
    Asm
      LDS SI,CmdLine
      CLD
    @@1:
      LODSB
      OR  AL,AL
      JE  @@2
      CMP AL,' '
      JBE @@1
    @@2:
      DEC SI
      MOV BX,SI
    @@3:
      LODSB
      CMP AL,' '
      JA  @@3
      DEC SI
      MOV AX,SI
      SUB AX,BX
      JE  @@4
      LOOP @@1
    @@4:
    End; { ArgStrCount /Windows }

    Function GetArgCount; assembler;
    { GETARGCOUNT - DOS service function
      Description: Returns the number of parameters passed to the
                   program on the command line
      Returns: Actual number of command line parameters }

    Asm
      PUSH DS
      XOR  CX,CX
      CALL ArgStrCount
      XCHG AX,CX
      NEG AX
      POP DS
    End; { GetArgCount /Windows }

    Function GetArgStr; assembler;
    { GETARGSTR - DOS service function
      Description: Returns the specified parameter from the command line
      Returns: ASCIIZ parameter, or an empty string if Index is less than zero
               or greater than GetArgCount. If Index is zero, GetArgStr returns
               the filename of the current module. The maximum length of the
               string returned in Dest is given by the MaxLen parameter. The
               returned value is Dest }

    Asm
      MOV CX,Index
      JCXZ @@2
      PUSH DS
      CALL ArgStrCount
      MOV SI,BX
      LES DI,Dest
      MOV CX,MaxLen
      CMP CX,AX
      JB  @@1
      XCHG AX,CX
    @@1:
      REP MOVSB
      XCHG AX,CX
      STOSB
      POP DS
      JMP @@3
    @@2:
      PUSH HInstance
      PUSH WORD PTR [Dest+2]
      PUSH WORD PTR [Dest]
      MOV AX,MaxLen
      INC AX
      PUSH AX
      CALL GetModuleFileName
    @@3:
      MOV AX,WORD PTR [Dest]
      MOV DX,WORD PTR [Dest+2]
    End; { GetArgStr /Windows }

    {$ELSE}

    Procedure ArgStrCount; assembler;
    Asm
      MOV DS,PrefixSeg
      MOV SI,80H
      CLD
      LODSB
      MOV DL,AL
      XOR DH,DH
      ADD DX,SI
    @@1:
      CMP SI,DX
      JE  @@2
      LODSB
      CMP AL,' '
      JBE @@1
      DEC SI
    @@2:
      MOV BX,SI
    @@3:
      CMP SI,DX
      JE  @@4
      LODSB
      CMP AL,' '
      JA  @@3
      DEC SI
    @@4:
      MOV AX,SI
      SUB AX,BX
      JE  @@5
      LOOP @@1
    @@5:
    End; { ArgStrCount }

    Function GetArgCount; assembler;
    { GETARGCOUNT - DOS service function
      Description: Returns the number of parameters passed to the
                   program on the command line
      Returns: Actual number of command line parameters }
    Asm
      PUSH DS
      XOR CX,CX
      CALL ArgStrCount
      XCHG AX,CX
      NEG AX
      POP DS
    End; { GetArgCount }

    Function GetArgStr; assembler;
    { GETARGSTR - DOS service function
      Description: Returns the specified parameter from the command line
      Returns: ASCIIZ parameter, or an empty string if Index is less than zero
               or greater than GetArgCount. If Index is zero, GetArgStr returns
               the filename of the current module. The maximum length of the
               string returned in Dest is given by the MaxLen parameter. The
               returned value is Dest }
    Asm
      PUSH DS
      MOV CX,Index
      JCXZ @@1
      CALL ArgStrCount
      MOV SI,BX
      JMP @@4
    @@1:
      MOV AH,30H
      INT DOS
      CMP AL,3
      MOV AX,0
      JB  @@4
      MOV DS,PrefixSeg
      MOV ES,DS:WORD PTR 2CH
      XOR DI,DI
      CLD
    @@2:
      CMP AL,ES:[DI]
      JE  @@3
      MOV CX,-1
      REPNE SCASB
      JMP @@2
    @@3:
      ADD DI,3
      MOV SI,DI
      PUSH ES
      POP DS
      MOV CX,256
      REPNE SCASB
      XCHG AX,CX
      NOT AL
    @@4:
      LES DI,Dest
      MOV CX,MaxLen
      CMP CX,AX
      JB  @@5
      XCHG AX,CX
    @@5:
      REP MOVSB
      XCHG AX,CX
      STOSB
      MOV AX,WORD PTR [Dest]
      MOV DX,WORD PTR [Dest+2]
      POP DS
    End; { GetArgStr }

    {$ENDIF}

    Function GetEnvVar;
    { GETENVVAR - DOS service function
      Description: Retrieves a specified DOS environment variable
      Returns: A pointer to the value of a specified variable,
               i.e. a pointer to the first character after the equals
               sign (=) in the environment entry given by VarName.
               VarName is case insensitive. GetEnvVar returns NIL if
               the specified environment variable does not exist }
    var
      L : word;
      P : PChar;
    Begin
      L := StrLen(VarName);
    {$IFDEF Windows}
      P := GetDosEnvironment;
    {$ELSE}
      P := Ptr(Word(Ptr(PrefixSeg, $2C)^), 0);
    {$ENDIF}
      while P^ <> #0 do
      begin
        if (StrLIComp(P, VarName, L) = 0) and (P[L] = '=') then
        begin
          GetEnvVar := P + L + 1;
          Exit;
        end;
        Inc(P, StrLen(P) + 1)
      end;
      GetEnvVar := nil
    End; { GetEnvVar }

    Function GetIntVec; assembler;
    { GETINTVEC - DOS service function
      Description: Retrieves the address stored in the specified interrupt vector
      Returns: A pointer to this address }
    Asm
      MOV AL,IntNo
      MOV AH,35H
      INT DOS
      MOV AX,ES
      LES DI,Vector
      CLD
      MOV DX,BX
      XCHG AX,BX
      STOSW
      XCHG AX,BX
      STOSW
      XCHG AX,DX
    End; { GetIntVec }

    Function SetIntVec; assembler;
    { SETINTVEC - DOS Service function
      Description: Sets the address in the interrupt vector table for the
                   specified interrupt
      Returns: The old address of the specified interrupt vector }
    Asm
      LES DI,Vector
      PUSH WORD PTR IntNo
      PUSH ES
      PUSH DI
      PUSH CS
      CALL GetIntVec
      PUSH DX
      PUSH AX
      PUSH DS
      LDS DX,Vector
      MOV AL,IntNo
      MOV AH,25H
      INT DOS
      POP DS
      POP AX
      POP DX
    End; { SetIntVec }

    Function GetDTA; assembler;
    { GETDTA - DOS service function
      Description: Retrieves a pointer address to a DOS data exchange buffer (DTA).
                   By default, DTA address has the offset PSP+80h and the size of
                   128 bytes. DTA is used to access files with the FCB method;
                   fn=2Fh
      Returns: A pointer address to DTA }
    Asm
      MOV AH,2Fh
      INT DOS
      MOV DX,BX { store offset }
      MOV AX,ES { store segment }
    End; { GetDTA }

    Function GetCurDisk; assembler;
    { GETCURDISK - DOS disk service function
      Description: Retrieves number of disk currently being active; fn=19h
      Returns: Default (current, active) disk number }
    Asm
      MOV AH,19h
      INT DOS
    End; { GetCurDisk }

    Function SetCurDisk; assembler;
    { SETCURDISK - DOS disk service function
      Description: Sets current (default/active) drive; fn=0Eh
      Returns: Number of disks in the system }
    Asm
      MOV AH,0Eh
      MOV DL,Drive
      INT DOS
    End; { SetCurDisk }

    Procedure GetDriveAllocInfo; assembler;
    { GETDRIVEALLOCINFO - DOS disk service function
      Description: Retrieves disk allocation information; fn=1Ch
      Retrieves Info structure }
    Asm
      PUSH DS
      MOV AH,1Ch
      MOV DL,Drive
      INT DOS
      MOV AH,BYTE PTR [DS:BX]
      LES DI,Info
      MOV BYTE PTR ES:[DI],AH      { Info.FATId }
      MOV WORD PTR ES:[DI+1],DX    { Info.Clusters }
      MOV BYTE PTR ES:[DI+3],AL    { Info.SectorsPerCluster }
      MOV WORD PTR ES:[DI+4],CX    { Info.BytesPerSector }
      POP DS
    End; { GetDriveAllocInfo }

    Function GetDPB; assembler;
    { GETDPB - DOS disk service function (undocumented)
      Description: Returns a block of information that is useful for applications
                   which perform sector-level access of disk drives supported by
                   device drivers; fn=32h
      Returns: 0 if successful, negative dosrInvalidDrive error code otherwise
      Remarks: Use 0 for default drive }
    Asm
      MOV DOSResult,dosrOk
      PUSH DS
      MOV AH,32h
      MOV DL,Drive
      INT DOS
      MOV WORD PTR [DPB],DS
      MOV WORD PTR [DPB+2],BX
      POP DS
      XOR AH,AH
      CMP AL,0FFh
      JNE @@1
      MOV DOSResult,dosrInvalidDrive
      PUSH DOSResult
      PUSH fnGetDPB
      CALL ErrorHandler
      MOV AX,DOSResult
      NEG AX
    @@1:
    End; { GetDPB }

    Function DiskSize; assembler;
    { DISKSIZE - DOS disk service function
      Description: Retrieves total disk size; fn=36h
      Returns: Total disk size in bytes if successful, negative dosrInvalidDrive
               error code otherwise
      Remarks: Use 0 for default drive }
    Asm
    @@1:
      MOV AH,36h
      MOV DL,Drive
      INT DOS
      CMP AX,0FFFFh
      JE  @@2
      MOV BX,DX
      IMUL CX
      IMUL BX
      JMP @@3
    @@2:
      MOV DOSResult,dosrInvalidDrive
      PUSH DOSResult
      PUSH fnGetDiskSize
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      MOV AX,DOSResult
      NEG AX
      XOR DX,DX
    @@3:
    End; { DiskSize }

    Function DiskFree; assembler;
    { DISKFREE - DOS disk service function
      Description: Retrieves amount of free disk space; fn=36h
      Returns: Amount of free disk space in bytes if successful,
               negative dosrInvalidDrive error code otherwise
      Remarks: Use 0 for default drive }
    Asm
    @@1:
      MOV AH,36h
      MOV DL,Drive
      INT DOS
      CMP AX,0FFFFh
      JE  @@2
      IMUL CX
      IMUL BX
      JMP @@3
    @@2:
      MOV DOSResult,dosrInvalidDrive
      PUSH DOSResult
      PUSH fnGetDiskFree
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      MOV AX,DOSResult
      NEG AX
      XOR DX,DX
    @@3:
    End; { DiskFree }

    Function IsFixedDisk; assembler;
    { ISFIXEDDISK - DOS disk service function
      Description: Ensures whether the specified disk is fixed or removable;
                   fn=4408h
      Returns: True, if the disk is fixed, False - otherwise
      Remarks: Use 0 for default (current) drive }
    Asm
      MOV AX,4408h
      MOV BL,Drive
      INT DOS
      JNC @@1
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnIsFixedDisk  { store function code }
      CALL ErrorHandler
    @@1:
    End; { IsFixedDisk }

    Function IsNetworkDrive; assembler;
    { ISNETWORKDRIVE - DOS disk service function
      Description: Ensures whether the specified disk drive is a network drive;
                   fn=4409h
      Returns: True if drive is a network drive, False if it's a local drive
      Remarks: Use 0 for detecting the default (current) drive }
    Asm
      MOV AX,4409h
      MOV BL,Drive
      INT DOS
      JNC @@1
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnIsNetworkDrive  { store function code }
      CALL ErrorHandler
    @@1:
    End; { IsNetworkDrive }

    Function GetDriveType(Drive : byte) : byte; assembler;
    { GETDRIVETYPE - Disk service function
      Description: Detects the type of the specified drive. Uses IsFixedDisk and
                   IsNetworkDrive functions to produce a result value
      Returns: One of (dt) constants (see const section)
      Remarks: Use 0 for detecting the default (current) drive }
    Asm
      PUSH WORD PTR Drive
      CALL IsNetworkDrive
      XOR BL,BL
      CMP DOSResult,dosrOk
      JNE @@3
      CMP AL,True
      JNE @@1
      MOV BL,dtRemote
      JMP @@3
    @@1:
      PUSH WORD PTR Drive
      CALL IsFixedDisk
      XOR BL,BL
      CMP DOSResult,dosrOk
      JNE @@3
      CMP AL,True
      JNE @@2
      MOV BL,dtFixed
      JMP @@3
    @@2:
      MOV BL,dtRemovable
    @@3:
      MOV AL,BL
    End; { GetDriveType }

    Function CreateDir; assembler;
    { CREATEDIR - DOS directory function
      Description: Creates a directory; fn=39h
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Dir
      MOV AH,39h
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnCreateDir  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { CreateDir }

    Function RemoveDir; assembler;
    { REMOVEDIR - DOS directory function
      Description: Removes (deletes) a directory; fn=3Ah
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Dir
      MOV AH,3Ah
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnRemoveDir  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { RemoveDir }

    Function GetCurDir; assembler;
    { GETCURDIR - DOS directory function
      Description: Retrieves current (active) directory name; fn=47h
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS SI,Dir
      MOV DL,Drive
      MOV AH,47h
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnGetCurDir  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { GetCurDir }

    Function SetCurDir; assembler;
    { SETCURDIR - DOS directory function
      Description: Sets current (active) directory; fn=3Bh
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Dir
      MOV AH,3Bh
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,AX
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnSetCurDir  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { SetCurDir }

    Function DeleteFile; assembler;
    { DELETEFILE - DOS file function
      Description: Deletes a file; fn=41h
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV AH,41h
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnDeleteFile  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { DeleteFile }

    Function RenameFile; assembler;
    { RENAMEFILE - DOS file function
      Description: Renames/moves a file; fn=56h
      Returns: 0 if successful, negative error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,OldPath
      LES DI,NewPath
      MOV AH,56h
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnRenameFile  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { RenameFile }

    Function ExistsFile; assembler;
    { EXISTSFILE - DOS file function
      Description: Determines whether the file exists; fn=4Eh
      Returns: TRUE if the file exists, FALSE - otherwise }
    Asm
      PUSH DS
      LDS DX,Path
      MOV AH,4Eh
      INT DOS
      POP DS
      JNC @@1
      XOR AL,AL
      JMP @@2
    @@1:
      MOV AL,True
    @@2:
    End; { ExistsFile }

    Function GetFileAttr; assembler;
    { GETFILEATTR - DOS file function
      Description: Gets file attributes; fn=43h,AL=0
      Returns: File attributes if no error, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV AX,4300h
      INT DOS
      POP DS
      JC  @@2
      MOV AX,CX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnGetFileAttr  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { GetFileAttr }

    Function SetFileAttr; assembler;
    { SETFILEATTR - DOS file function
      Description: Sets file attributes; fn=43h,AL=1
      Returns: 0 if no error, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV CX,Attr
      MOV AX,4301h
      INT DOS
      POP DS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnSetFileAttr  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { GetFileAttr }

    Function FindFirst; assembler;
    { FINDFIRST - DOS file service function
      Description: Searches the specified (or current) directory for
                   the first entry that matches the specified filename and
                   attributes; fn=4E00h
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,F
      MOV AH,1AH
      INT DOS
      POP DS
      LES DI,Path
      MOV CX,Attr
      MOV AH,4EH
      CALL AnsiDosFunc
      MOV DOSResult,dosrOk
      JC  @@2
    {$IFDEF Windows}
      LES DI,F
      ADD DI,OFFSET TSearchRec.Name
      PUSH ES
      PUSH DI
      PUSH ES
      PUSH DI
      CALL OemToAnsi
    {$ENDIF}
      XOR AX,AX
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnFindFirst  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
    @@3:
      NEG AX
    End; { FindFirst }

    Function FindNext; assembler;
    { FINDNEXT - DOS file service function
      Description: Returs the next entry that matches the name and
                   attributes specified in a previous call to FindFirst.
                   The search record must be one passed to FindFirst
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      PUSH DS
      LDS DX,F
      MOV AH,1AH
      INT DOS
      POP DS
      MOV AH,4FH
      MOV DOSResult,dosrOk
      INT DOS
      JC  @@2
    {$IFDEF Windows}
      LES DI,F
      ADD DI,OFFSET TSearchRec.Name
      PUSH ES
      PUSH DI
      PUSH ES
      PUSH DI
      CALL OemToAnsi
    {$ENDIF}
      XOR AX,AX
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnFindNext  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
    @@3:
      NEG AX
    End; { FindNext }

    Procedure UnpackTime; assembler;
    { UNPACKTIME - Service function
      Description: Converts a 4-byte packed date/time returned by
                   FindFirst, FindNext or GetFTime into a TDateTime record }
    Asm
      LES DI,T
      CLD
      MOV AX,WORD PTR [P+2]
      MOV CL,9
      SHR AX,CL
      ADD AX,1980
      STOSW
      MOV AX,WORD PTR [P+2]
      MOV CL,5
      SHR AX,CL
      AND AX,15
      STOSW
      MOV AX,WORD PTR [P+2]
      AND AX,31
      STOSW
      MOV AX,P.Word[0]
      MOV CL,11
      SHR AX,CL
      STOSW
      MOV AX,WORD PTR [P+2]
      MOV CL,5
      SHR AX,CL
      AND AX,63
      STOSW
      MOV AX,WORD PTR [P]
      AND AX,31
      SHL AX,1
      STOSW
    End; { UnpackTime }

    Function PackTime; assembler;
    { PACKTIME - Service function
      Decription: Converts a TDateTime record into a 4-byte packed
                  date/time used by SetFTime
      Returns: 4-byte long integer corresponding to packed date/time }
    Asm
      PUSH DS
      LDS SI,T
      CLD
      LODSW
      SUB AX,1980
      MOV CL,9
      SHL AX,CL
      XCHG AX,DX
      LODSW
      MOV CL,5
      SHL AX,CL
      ADD DX,AX
      LODSW
      ADD DX,AX
      LODSW
      MOV CL,11
      SHL AX,CL
      XCHG AX,BX
      LODSW
      MOV CL,5
      SHL AX,CL
      ADD BX,AX
      LODSW
      SHR AX,1
      ADD AX,BX
      POP DS
    End; { PackTime }

    Function h_CreateFile; assembler;
    { H_CREATEFILE - DOS Handle file function
      Description: Creates a file; fn=3Ch
      Returns: File handle if successful, 0 if unsuccessful }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV CX,0
      MOV AH,5Bh
      INT DOS
      POP DS
      JC  @@2
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnCreateFile  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      XOR AX,AX
    @@3:
    End; { h_CreateFile }

    Function h_CreateTempFile; assembler;
    { H_CREATETEMPFILE - DOS Handle file function
      Description: Creates a temporary file; fn=5Ah
      Returns: File handle if successful, 0 if unsuccessful }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV CX,0 { file attribute here, 0 used for normal }
      MOV AH,5Ah
      INT DOS
      POP DS
      JC  @@2
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnCreateTempFile  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      XOR AX,AX
    @@3:
    End; { h_CreateTempFile }

    Function h_OpenFile; assembler;
    { H_OPENFILE - DOS Handle file function
      Description: Opens a file for input, output or input/output; fn=3Dh
      Returns: File handle if successful, 0 if unsuccessful }
    Asm
    @@1:
      PUSH DS
      LDS DX,Path
      MOV AH,3Dh
      MOV AL,Mode
      INT DOS
      POP DS
      JC  @@2
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnOpenFile  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      XOR AX,AX
    @@3:
    End; { h_OpenFile }

    Function h_Read; assembler;
    { H_READ - DOS Handle file function
      Description: Reads a memory block from file; fn=3Fh
      Returns: Actual number of bytes read }
    Asm
    @@1:
      PUSH DS
      LDS DX,Buffer
      MOV CX,Count
      MOV BX,Handle
      MOV AH,3Fh
      INT DOS
      POP DS
      MOV DOSResult,dosrOk
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnRead  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
    @@2:
    End; { h_Read }

    Function h_Write; assembler;
    { H_WRITE - DOS Handle file function
      Description: Writes a memory block to file; fn=40h
      Returns: Actual number of bytes written }
    Asm
    @@1:
      PUSH DS
      LDS DX,Buffer
      MOV CX,Count
      MOV BX,Handle
      MOV AH,40h
      INT DOS
      POP DS
      MOV DOSResult,dosrOk
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnWrite  { store function code }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
    @@2:
    End; { h_Write }

    Function h_Seek; assembler;
    { H_SEEK - DOS Handle file function
      Description: Seeks to a specified file position; fn=42h
                   Start is one of the (sk) constants and points to a relative
                   seek offset position
      Returns: Current file position if successful, 0 - otherwise }
    Asm
    @@1:
      MOV CX,WORD PTR [SeekPos+2]
      MOV DX,WORD PTR [SeekPos]
      MOV BX,Handle
      MOV AL,Start
      MOV AH,42h
      MOV DOSResult,dosrOk
      INT DOS
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnSeek  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
    @@2:
    End; { h_Seek }

    Function h_FilePos;
    { H_GETPOS - DOS Handle file function
      Description: Calls h_Seek to determine file active position
      Returns: Current file (seek) position number in long integer }
    Begin
      h_FilePos := h_Seek(Handle, 0, skPos)
    End; { h_FilePos }

    Function h_FileSize;
    { H_FILESIZE - DOS Handle file function
      Description: Determines file size
      Returns: File size in bytes }
    var SavePos, Size : longint;
    Begin
      SavePos := h_FilePos(Handle);
      h_FileSize := h_Seek(Handle, 0, skEnd);
      h_Seek(Handle, SavePos, skStart)
    End; { h_FileSize }

    Function h_Eof; assembler;
    { H_EOF - DOS Handle file function
      Description: Checks if the current file position is equal to file size
                   and then returns True
      Returns: True if end of file detected, False - otherwise }
    var Size : longint;
    Asm
      PUSH Handle
      CALL h_FileSize               { Get file size in AX:DX }
      MOV WORD PTR [Size],AX        { Store high word }
      MOV WORD PTR [Size+2],DX      { Store low word }
      PUSH Handle
      CALL h_FilePos                 { Get current file position }
      XOR CL,CL
      CMP AX,WORD PTR [Size]
      JNE @@1
      CMP DX,WORD PTR [Size+2]
      JNE @@1
      MOV CL,True
    @@1:
      MOV AL,CL
    End; { h_GetPos }

    Function h_GetFTime; assembler;
    { H_GETFTIME - DOS Handle file function
      Description: Returns file update date and time values; fn=5700h
      Returns: Date and time values in long integer
               or negative DOS error code if an error occured }
    Asm
    @@1:
      MOV BX,Handle
      MOV AX,5700h { read date and time }
      MOV DOSResult,dosrOk
      INT DOS
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnGetFDateTime  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@2:
    End; { h_GetFTime }

    Function h_SetFTime; assembler;
    { H_SETFTIME - DOS Handle file function
      Description: Sets file date and time; fn=5701h
      Returns: New date and time values in long integer
               or negative DOS error code if an error occured }
    Asm
    @@1:
      MOV CX,WORD PTR [DateTime]
      MOV DX,WORD PTR [DateTime+2]
      MOV BX,Handle
      MOV AX,5701h { read date and time }
      MOV DOSResult,dosrOk
      INT DOS
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnSetFDateTime  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@2:
    End; { h_SetFTime }

    Function h_CloseFile; assembler;
    { H_CLOSEFILE - DOS Handle file function
      Description: Closes open file; fn=3Eh
      Returns: 0 if successful, negative DOS error code otherwise }
    Asm
    @@1:
      MOV BX,Handle
      MOV AH,3Eh
      INT DOS
      JC  @@2
      XOR AX,AX
      MOV DOSResult,dosrOk
      JMP @@3
    @@2:
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnCloseFile  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      POP AX
      NEG AX
    @@3:
    End; { h_CloseFile }

    Function MemAlloc; assembler;
    Asm
    @@1:
      MOV DOSResult,dosrOk
      MOV AX,WORD PTR [Size]
      MOV DX,WORD PTR [Size+2]
      MOV CX,16
      DIV CX
      INC AX
      MOV BX,AX
      MOV AH,48h
      INT DOS
      JNC @@2
      MOV DOSResult,AX { save error code in global variable }
      PUSH AX     { store error code }
      PUSH fnMemAlloc  { store function number }
      CALL ErrorHandler
      CMP AL,frRetry
      JE  @@1
      XOR AX,AX
    @@2:
      MOV DX,AX
      XOR AX,AX
    End; { MemAlloc }

    Function MemFree; assembler;
    Asm
      MOV DOSResult,dosrOk
      MOV ES,WORD PTR [P+2]
      MOV AH,49h
      INT DOS
      JNC @@1
      MOV DOSResult,AX
      PUSH AX
      PUSH fnMemFree
      CALL ErrorHandler
    @@1:
      MOV AX,DOSResult
      NEG AX
    End; { MemFree }

    Function FileSearch; assembler;
    { FileSearch searches for the file given by Name in the list of }
    { directories given by List. The directory paths in List must   }
    { be separated by semicolons. The search always starts with the }
    { current directory of the current drive. If the file is found, }
    { FileSearch stores a concatenation of the directory path and   }
    { the file name in Dest. Otherwise FileSearch stores an empty   }
    { string in Dest. The maximum length of the result is defined   }
    { by the fsPathName constant. The returned value is Dest.       }
    Asm
      PUSH DS
      CLD
      LDS SI,List
      LES DI,Dest
      MOV CX,fsPathName
    @@1:
      PUSH DS
      PUSH SI
      JCXZ @@3
      LDS SI,Name
    @@2:
      LODSB
      OR  AL,AL
      JE  @@3
      STOSB
      LOOP @@2
    @@3:
      XOR AL,AL
      STOSB
      LES DI,Dest
      MOV AX,4300H
      CALL AnsiDosFunc
      POP SI
      POP DS
      JC  @@4
      TEST CX,18H
      JE  @@9
    @@4:
      LES DI,Dest
      MOV CX,fsPathName
      XOR AH,AH
      LODSB
      OR  AL,AL
      JE  @@8
    @@5:
      CMP AL,';'
      JE  @@7
      JCXZ @@6
      MOV AH,AL
      STOSB
      DEC CX
    @@6:
      LODSB
      OR  AL,AL
      JNE @@5
      DEC SI
    @@7:
      JCXZ @@1
      CMP AH,':'
      JE  @@1
      MOV AL,'\'
      CMP AL,AH
      JE  @@1
      STOSB
      DEC CX
      JMP @@1
    @@8:
      STOSB
    @@9:
      MOV AX,WORD PTR [Dest]
      MOV DX,WORD PTR [Dest+2]
      POP DS
    End; { FileSearch }

    Function FileExpand; assembler;
    { FileExpand fully expands the file name in Name, and stores    }
    { the result in Dest. The maximum length of the result is       }
    { defined by the fsPathName constant. The result is an all }
    { upper case string consisting of a drive letter, a colon, a }
    { root relative directory path, and a file name. Embedded '.' }
    { and '..' directory references are removed, and all name and }
    { extension components are truncated to 8 and 3 characters. The }
    { returned value is Dest.                }

    Asm
      PUSH DS
      CLD
      LDS SI,Name
      LEA DI,TempStr
      PUSH SS
      POP ES
      LODSW
      OR  AL,AL
      JE  @@1
      CMP AH,':'
      JNE @@1
      CMP AL,'a'
      JB  @@2
      CMP AL,'z'
      JA  @@2
      SUB AL,20H
      JMP @@2
    @@1:
      DEC SI
      DEC SI
      MOV AH,19H
      INT DOS
      ADD AL,'A'
      MOV AH,':'
    @@2:
      STOSW
      CMP [SI].Byte,'\'
      JE  @@3
      SUB AL,'A'-1
      MOV DL,AL
      MOV AL,'\'
      STOSB
      PUSH DS
      PUSH SI
      MOV AH,47H
      MOV SI,DI
      PUSH ES
      POP DS
      INT DOS
      POP SI
      POP DS
      JC  @@3
      XOR AL,AL
      CMP AL,ES:[DI]
      JE  @@3
    {$IFDEF Windows}
      PUSH ES
      PUSH ES
      PUSH DI
      PUSH ES
      PUSH DI
      CALL OemToAnsi
      POP ES
    {$ENDIF}
      MOV CX,0FFFFH
      XOR AL,AL
      CLD
      REPNE SCASB
      DEC DI
      MOV AL,'\'
      STOSB
    @@3:
      MOV CX,8
    @@4:
      LODSB
      OR  AL,AL
      JE  @@7
      CMP AL,'\'
      JE  @@7
      CMP AL,'.'
      JE  @@6
      JCXZ @@4
      DEC CX
    {$IFNDEF Windows}
      CMP AL,'a'
      JB  @@5
      CMP AL,'z'
      JA  @@5
      SUB AL,20H
    {$ENDIF}
    @@5:
      STOSB
      JMP @@4
    @@6:
      MOV CL,3
      JMP @@5
    @@7:
      CMP ES:[DI-2].Word,'.\'
      JNE @@8
      DEC DI
      DEC DI
      JMP @@10
    @@8:
      CMP ES:[DI-2].Word,'..'
      JNE @@10
      CMP ES:[DI-3].Byte,'\'
      JNE @@10
      SUB DI,3
      CMP ES:[DI-1].Byte,':'
      JE  @@10
    @@9:
      DEC DI
      CMP ES:[DI].Byte,'\'
      JNE @@9
    @@10:
      MOV CL,8
      OR  AL,AL
      JNE @@5
      CMP ES:[DI-1].Byte,':'
      JNE @@11
      MOV AL,'\'
      STOSB
    @@11:
      LEA SI,TempStr
      PUSH SS
      POP DS
      MOV CX,DI
      SUB CX,SI
      CMP CX,79
      JBE @@12
      MOV CX,79
    @@12:
      LES DI,Dest
      PUSH ES
      PUSH DI
    {$IFDEF Windows}
      PUSH ES
      PUSH DI
    {$ENDIF}
      REP MOVSB
      XOR AL,AL
      STOSB
    {$IFDEF Windows}
      CALL AnsiUpper
    {$ENDIF}
      POP AX
      POP DX
      POP DS
    End; { FileExpand }

    {$W+}
    Function FileSplit;
    { FileSplit splits the file name specified by Path into its     }
    { three components. Dir is set to the drive and directory path  }
    { with any leading and trailing backslashes, Name is set to the }
    { file name, and Ext is set to the extension with a preceding   }
    { period. If a component string parameter is NIL, the           }
    { corresponding part of the path is not stored. If the path     }
    { does not contain a given component, the returned component    }
    { string is empty. The maximum lengths of the strings returned  }
    { in Dir, Name, and Ext are defined by the fsDirectory,         }
    { fsFileName, and fsExtension constants. The returned value is  }
    { a combination of the fcDirectory, fcFileName, and fcExtension }
    { bit masks, indicating which components were present in the    }
    { path. If the name or extension contains any wildcard          }
    { characters (* or ?), the fcWildcards flag is set in the       }
    { returned value.                                               }
    var
      DirLen, NameLen, Flags : word;
      NamePtr, ExtPtr : PChar;
    begin
      NamePtr := StrRScan(Path, '\');
      if NamePtr = nil then NamePtr := StrRScan(Path, ':');
      if NamePtr = nil then NamePtr := Path else Inc(NamePtr);
      ExtPtr := StrScan(NamePtr, '.');
      if ExtPtr = nil then ExtPtr := StrEnd(NamePtr);
      DirLen := NamePtr - Path;
      if DirLen > fsDirectory then DirLen := fsDirectory;
      NameLen := ExtPtr - NamePtr;
      if NameLen > fsFilename then NameLen := fsFilename;
      Flags := 0;
      if (StrScan(NamePtr, '?') <> nil) or (StrScan(NamePtr, '*') <> nil) then
        Flags := fcWildcards;
      if DirLen <> 0 then Flags := Flags or fcDirectory;
      if NameLen <> 0 then Flags := Flags or fcFilename;
      if ExtPtr[0] <> #0 then Flags := Flags or fcExtension;
      if Dir <> nil then StrLCopy(Dir, Path, DirLen);
      if Name <> nil then StrLCopy(Name, NamePtr, NameLen);
      if Ext <> nil then StrLCopy(Ext, ExtPtr, fsExtension);
      FileSplit := Flags;
    End; { FileSplit }
    {$W-}

    Function StdErrorProc(ErrCode : integer; FuncCode : word) : byte; far;
    assembler;
    { Default error handler procedure called from EnhDOS functions }
    Asm
      MOV AL,frOk   { Return zero }
    End; { StdErrorProc }

    const WrongDOSVersion : PChar = 'DOS 3.1 or greater required.'#13#10'$';

    Begin
      asm
        MOV AH,30h { Get DOS version }
        INT DOS
        CMP AL,3
        JGE @@continue { if greater than or equal to 3 then continue else exit }
        PUSH DS
        LDS DX,WrongDOSVersion
        MOV AH,09h
        INT DOS
        MOV AH,4Ch
        INT DOS
      @@continue:
        LES DI,Copyright
      end;
      DOSResult := dosrOk;
      SetErrorHandler(StdErrorProc)
    End. { EnhDOS+ }

    { -------------------------------------   DEMO ------------------   }
    { ***** ENHDDEMO.PAS ***** }

    Program DemoEnhDOS;
    { Copyright (c) 1994 by Andrew Eigus   Fido Net 2:5100/33 }
    { EnhDOS+ (Int21) demo program }

    {$M 8192,0,0}
    { no heap size, couz using own memeory allocation }

    (* Simple copy file program *)

    uses EnhDOS, Strings;

    const BufSize = 65535; { may be larger; you may allocate more }

    var
      Buffer : pointer;
      InputFile, OutputFile : array[0..63] of Char;
      Handle1, Handle2 : THandle;
      BytesRead : word;

    Function Int21ErrorHandler(ErrCode : integer; FuncCode : word) : byte; far;
    var fn : array[0..20] of Char;
    Begin
      case FuncCode of
        fnOpenFile: StrCopy(fn, 'h_OpenFile');
        fnCreateFile: StrCopy(fn, 'h_CreateFile');
        fnRead: StrCopy(fn, 'h_Read');
        fnWrite: StrCopy(fn, 'h_Write');
        fnSeek: StrCopy(fn, 'h_Seek');
        fnCloseFile: StrCopy(fn, 'h_CloseFile');
        fnMemAlloc: StrCopy(fn, 'MemAlloc');
        fnDeleteFile: Exit;
        else fn[0] := #0
      end;
      WriteLn('DOS Error ', ErrCode, ' in function ', FuncCode, ' (', fn, ')');
      { actually for function return code see fr consts in the EnhDOS const
        section }
    End; { Int21ErrorHandler }

    Begin
      SetErrorHandler(Int21ErrorHandler);

      WriteLn('EnhDOS+ demo program: copies one file to another');
      repeat
        if ParamCount > 0 then
          StrPCopy(InputFile, ParamStr(1))
        else
        begin
          Write('Enter file name to read from: ');
          ReadLn(InputFile)
        end;
        if ParamCount > 1 then
          StrPCopy(OutputFile, ParamStr(2))
        else
        begin
          Write('Enter file name to write to:  ');
          ReadLn(OutputFile)
        end;
        WriteLn
      until (StrLen(InputFile) > 0) and (StrLen(OutputFile) > 0);

      if not ExistsFile(InputFile) then
      begin
        WriteLn('File not found: ', InputFile);
        Halt(1)
      end;

      Buffer := MemAlloc(BufSize);

      Write('Copying... ');

      Handle1 := h_OpenFile(InputFile, omRead);
      if Handle1 <> 0 then
      begin
        DeleteFile(OutputFile);
        Handle2 := h_CreateFile(OutputFile);
        if Handle2 <> 0 then
        begin
          BytesRead := 1;

          while (BytesRead > 0) and (DOSResult = dosrOk) do
          begin
            BytesRead := h_Read(Handle1, Buffer^, BufSize);

            if DOSResult <> dosrOk then
              { read error then }
              WriteLn('Error reading from input file');

            if h_Write(Handle2, Buffer^, BytesRead) <> BytesRead then
              { write error then }
            begin
              WriteLn('Error writing to output file');
              DOSResult := $FF
            end
          end;
          if DOSResult = dosrOk then WriteLn('File copied OK');
          h_CloseFile(Handle2)
        end;
        h_CloseFile(Handle1)
      end;

      MemFree(Buffer)
    End. { DemoEnhDOS }

[Back to DOS SWAG index] [Back to Main SWAG index] [Original]