Here's a unit that will let you open more than the default
maximum number of files (up to about 200, FILES statement
in CONFIG.SYS permitting) :

{$IfDef DPMI}
     {$c Moveable PreLoad Discardable}

Unit ExtHandl ;


{$IfNDef DPMI}
Procedure DOSRealloc(p : Pointer) ;


{$IfNDef DPMI}
Procedure DOSRealloc(p : Pointer) ; Assembler ;
{ This procedures changes the size of the memory block allocated to the
  program. It proceeds the same way than the SetMemTop procedure of the
  Memory unit does. }
     Mov  BX, Word Ptr [p]                   { offset }
     Add  BX, $0f
     Mov  CL, 4
     ShR  BX, CL
     Add  BX, Word Ptr [p+2]                 { segment }
     Mov  AX, PrefixSeg
     Sub  BX, AX
     Mov  ES, AX
     { ES contains the program's PSP, BX is the new number of paragraphs
       of the memory block. }
     Mov  AH, $4a                            { Modify memory allocation }
     Int  $21
End ;

     FreeParas      = 1024 Div 16 ;     { We're going to make sure DOS     }
                                        { still has some RAM               }
     Reallocating   = 1 ;
     TPLacksRAM     = 2 ;
     GettingHandles = 3 ;

Procedure PrintError(ErrNo : Word ; Location : Word) ;
     WriteLn(ParamStr(0)) ;
     WriteLn('ExtHandl : Startup error #', ErrNo, ', location=', Location) ;
     WriteLn('MaxAvail=', MaxAvail) ;
     Write('[Enter] pour continuer...') ;
     ReadLn ;
End ;

     { This initialisation code makes sure DOS is still able to assume
       memory allocation requests that are likely to occur (whether by
       Spawno or a "Set Handle Count" request).
       This allows the main program to use whatever $m statement it needs. }
{$IfNDef DPMI}
          Mov  AH, $48                  { Allocate memory }
          Mov  BX, $ffff                { More than DOS can give us }
          Int  $21
          Cmp  BX, FreeParas            { BX contains avail paragraphs }
          JNC  @DOSHasEnough
          { Let's check that the heap contains more than needed bytes }
          Mov  AX, Word Ptr [HeapPtr+2]
          Add  AX, FreeParas
          Cmp  AX, Word Ptr [HeapEnd+2]
          JNC  @NotEnoughRAM            { Actually, TP hasn't enough }
          Sub  Word Ptr [HeapEnd+2], FreeParas
          Push Word Ptr [HeapEnd+2]
          Push Word Ptr [HeapEnd]
          Call DOSRealloc
          JNC  @DOSHasEnough
          Push AX
          Mov  AX, Reallocating
          Push AX
          Call PrintError
          Jmp  #DOSHasEnough
          XOr  AX, AX
          Push AX
          Mov  AX, TPLacksRAM
          Push AX
          Call PrintError
          { Augmentation du nombre de handles disponibles }
          Mov  AH, $67                  { Extend handles }
          Mov  BX, 199                  { Gi'me 2 hundreds of 'em }
          Int  $21
          JNC  @PasDErreur              { Si CF=1, erreur }
          Push AX
          Mov  AX, GettingHandles
          Push AX
          Call PrintError
     End ;