Contributor: MARTIN RICHARDSON        

{
Here is my DRIVES routine again to return all valid drive letters on a
PC.  This is a fix from the last version which incorrectly addressed
the local variables and wound up hosing memory.  I also added some
extensive comments for readability.  Enjoy! }

{*****************************************************************************
 * Function ...... Drives
 * Purpose ....... To return a string containing the valid drives for the
 *                 current system.
 * Parameters .... None
 * Returns ....... A string of the valid drive letters.
 * Notes ......... Rather than changing to each drive to see if it exists, we
 *                 can instead call DOS Function 26h - Parse a file name.
 *                 If the file name is invalid (eg, F:), then DOS will say
 *                 so.  So, by testing each drive letter as a file name,
 *                 DOS will tell us which are good and which are bad!
 * Author ........ Martin Richardson
 * Date .......... August 6, 1993
 * Update ........ 02-01-94: Corrected problem where local VAR variables were
 *                           not being used, but a random memory location was
 *                           instead!
 *                         : Added comments for clarity.
 *****************************************************************************}
FUNCTION Drives: STRING; ASSEMBLER;
VAR
   DriveInfo:   ARRAY[1..2]  OF CHAR;
   Buffer:      ARRAY[1..40] OF CHAR;
   DriveString: ARRAY[1..25] OF CHAR;
ASM
     PUSH   SI                     { Save Important Registers }
     PUSH   DI
     PUSH   ES
     PUSH   DS

     MOV    SI, SS                 { The Stack Segment (SS) points to the }
     MOV    DS, SI                 { VAR's above.  Point DS to it... }
     PUSH   DS
     POP    ES                     { ...and ES as well. }

     LEA    SI, DriveInfo          { DS:SI - Where we test each drive letter }
     LEA    DI, Buffer             { ES:DI - FCB Buffer }
     LEA    BX, DriveString        { DS:BX - Our resultant string }

     MOV    BYTE PTR [SI], '@'     { The character before 'A' }
     XOR    CX, CX                 { Zero out CX }

@Scan:
     INC    BYTE PTR [SI]          { Next Drive Letter }
     MOV    BYTE PTR [SI+1], ':'
     MOV    AX, $2906              { DOS Function 29h - Parse Filename }
     INT    21h                    {   DS:SI - String to be parsed }
                                   {   ES:DI - FCB }
     LEA    SI, DriveInfo          { DS:SI }
     CMP    AL, $FF                { AL = FFh if function fails (invalid }
     JE     @NotValid              {     drive letter) }

     INC    CX                     { Add one more to our string length... }
     PUSH   CX                     { ...and save it. }
     MOV    CL, BYTE PTR DS:[SI]   { Grab the valid drive letter... }
     MOV    [BX], CL               { ...and stuff it into our result }
     INC    BX                     { Next position in result string }
     POP    CX                     { Get our length counter back }

@NotValid:
     CMP    BYTE PTR [SI], 'Z'     { Did we go through all letters? }
     JNE    @Scan                  { Nope, so next letter }

     LEA    SI, DriveString        { Store DriveString to #Result }
     LES    DI, @Result
     INC    DI
     REP    MOVSB

     XCHG   AX, DI                 { This is the only way to store the }
     MOV    DI, WORD PTR @Result   {   length that I can get to work. }
     SUB    AX, DI
     DEC    AX
     STOSB

     POP    DS                     { Restore Important Registers }
     POP    ES
     POP    DI
     POP    SI
END;