home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / virtmem.exe / EMS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-06-05  |  18.5 KB  |  547 lines

  1. unit EMS;
  2. {$O+}
  3. {$F+}
  4.  
  5. { *************************************************************
  6.   * This unit provides an interface to the basic functions of *
  7.   * the LIM Expanded Memory Specification. Since it does not  *
  8.   * use any of the LIM EMS 4.0 function calls, you can also   *
  9.   * use it on systems with EMS versions less than 4.0         *
  10.   ************************************************************* }
  11.  
  12. { Written by:
  13.     Peter Immarco.
  14.     Thought Dynamics
  15.     Manhattan Beach, CA
  16.     Compuserve ID# 73770,123
  17.      *** Public Domain ***
  18.  
  19.   Used by permission of the author.
  20. }
  21.  
  22. {Revised and made into a unit by Wayne Knorr}
  23.  
  24. { This unit provides the following functions:
  25.   +------------------------------------------------------------+
  26.   | * Makes sure the LIM Expanded Memory Manager (EMM) has     |
  27.   |   been installed in memory                                 |
  28.   | * Displays the version number of the EMM present in memory |
  29.   | * Determines if there are enough pages (16k blocks) of     |
  30.   |   memory for our test program's usage. It then displays    |
  31.   |   the total number of EMS pages present in the system,     |
  32.   |   and how many are available for our usage                 |
  33.   | * Requests the desired number of pages from the EMM        |
  34.   | * Maps a logical page onto one of the physical pages given |
  35.   |   to us                                                    |
  36.   | * Displays the base address of our EMS memory page frame   |
  37.   | * Returns the EMS memory given to us back to the EMM, and  |
  38.   |   exits                                                    |
  39.   +------------------------------------------------------------|}
  40.  
  41.  
  42. { All the calls are structured to return the result or error
  43.   code of the Expanded Memory function performed as an integer.
  44.   If the error code is not zero, which means the call failed,
  45.   a simple error procedure is called.}
  46.  
  47. Interface
  48.  
  49. uses CRT,DOS;
  50.  
  51. Const
  52.  
  53.   SizeOPhysicalPage=16*1024;
  54.   MaxPhysicalPage  =35;                                                      {Expected max mappable physical EMS page}
  55.  
  56. Type
  57.  
  58.      PhysicalPageRec=
  59.         Record
  60.           PhysPageSegment:Word;
  61.           PhysPageNumber :Word;
  62.         End;
  63.      PhysicalPageArr=Array [0..MaxPhysicalPage] of PhysicalPageRec;
  64.  
  65. Var
  66.  
  67.      EMSUseful    :Boolean;                  {Flag: We can use EMS}
  68.      EMSPageBase  :Word;                     {Current EMS page base}
  69.      EMSHandl     :Word;                     {EMS page handle for deallocation.}
  70.      EMSPageAvail :Word;                     {Number of logical Pages.}
  71.      NumOPhysicalPage:Word;                  {Number of physical pages.}
  72.      PhysicalPage    :PhysicalPageArr;       {Array of all valid physical pages.}
  73.  
  74. Procedure EMSInit(OverRide:Boolean);
  75. Function EMS_Pages_Available
  76.     (Var Total_EMS_Pages,Pages_Available: Word): Word;
  77. Function Allocate_Expanded_Memory_Pages
  78.     (Pages_Needed: Word; Var Handle: Word   ): Word;
  79. Function Map_Expanded_Memory_Pages
  80.     (Handle,Logical_Page,Physical_Page: Word): Word;
  81. Function Get_Page_Frame_Base_Address
  82.     (Var Page_Frame_Address: Word): Word;
  83. Function Deallocate_Expanded_Memory_Pages
  84.     (Handle: Word): Word;
  85.  
  86. Implementation
  87.  
  88. Type
  89.   ST3  = string[3];
  90.   ST80 = string[80];
  91.   ST5 = string[5];
  92.  
  93. Const
  94.   EMM_INT                   = $67;
  95.   DOS_Int                   = $21;
  96.   GET_PAGE_FRAME            = $41;
  97.   GET_UNALLOCATED_PAGE_COUNT= $42;
  98.   ALLOCATE_PAGES            = $43;
  99.   MAP_PAGES                 = $44;
  100.   DEALLOCATE_PAGES          = $45;
  101.   GET_VERSION               = $46;
  102.   GETMAPPHYADDARR           = $5800;
  103.   STATUS_OK                 = 0;
  104.  
  105.  
  106. { * --------------------------------------------------------- * }
  107.   { The function Hex_String converts an Word into a four
  108.     character hexadecimal number(string) with leading zeroes.   }
  109.   Function Hex_String(Number: Word): ST5;
  110.     Function Hex_Char(Number: Word): Char;
  111.     Begin
  112.       If Number<10 then
  113.         Hex_Char:=Char(Number+48)
  114.       else
  115.         Hex_Char:=Char(Number+55);
  116.     end; { Function Hex_Char }
  117.  
  118.   Var
  119.     S: ST5;
  120.   Begin
  121.     S:='';
  122.     S:=Hex_Char( (Number shr 1) div 2048);
  123.     Number:=( ((Number shr 1) mod 2048) shl 1)+
  124.             (Number and 1) ;
  125.     S:=S+Hex_Char(Number div 256);
  126.     Number:=Number mod 256;
  127.     S:=S+Hex_Char(Number div 16);
  128.     Number:=Number mod 16;
  129.     S:=S+Hex_Char(Number);
  130.     Hex_String:=S+'h';
  131.   end; { Function Hex_String }
  132.  
  133. { * --------------------------------------------------------- * }
  134.  
  135.   { The function Emm_Installed checks to see if the Expanded
  136.     Memory Manager (EMM) is loaded in memory. It does this by
  137.     looking for the string 'EMMXXXX0', which should be located
  138.     at 10 bytes from the beginning of the code segment pointed
  139.     to by the EMM interrupt, 67h                                }
  140.   Function Emm_Installed: Boolean;
  141.     Var
  142.       Emm_Device_Name       : string[8];
  143.       Int_67_Device_Name    : string[8];
  144.       Position              : Word;
  145.       Regs                  : registers;
  146.  
  147.   Begin
  148.     Int_67_Device_Name:='';
  149.     Emm_Device_Name   :='EMMXXXX0';
  150.     with Regs do
  151.     Begin
  152.       { Get the code segment pointed to by Interrupt 67h, the EMM
  153.       interrupt by using DOS call $35, 'get interrupt vector'     }
  154.       AH:=$35;
  155.       AL:=EMM_INT;
  156.       Intr(DOS_int,Regs);
  157.  
  158.       { The ES pseudo-register contains the segment address pointed
  159.         to by Interrupt 67h }
  160.       { Create an 8 character string from the 8 successive bytes
  161.         pointed to by ES:$0A (10 bytes from ES)                   }
  162.       For Position:=0 to 7 do
  163.         Int_67_Device_Name:=
  164.           Int_67_Device_Name+Chr(mem[ES:Position+$0A]);
  165.       Emm_Installed:=True;
  166.       { Is it the EMM manager signature, 'EMMXXXX0'? then EMM is
  167.         installed and ready for use, if not, then the EMM manager
  168.         is not present                                            }
  169.       If Int_67_Device_Name<>Emm_Device_Name
  170.         then Emm_Installed:=False;
  171.     end; { with Regs do }
  172.   end;  { Function Emm_Installed }
  173.  
  174. { * --------------------------------------------------------- * }
  175.  
  176.   { This function returns the total number of EMS pages present
  177.     in the system, and the number of EMS pages that are
  178.     available for our use                                       }
  179.   Function EMS_Pages_Available
  180.     (Var Total_EMS_Pages,Pages_Available: Word): Word;
  181.   Var
  182.     Regs: Registers;
  183.   Begin
  184.     with Regs do
  185.     Begin
  186.       { Put the desired EMS function number in the AH pseudo-
  187.         register                                                }
  188.       AH:=Get_Unallocated_Page_Count;
  189.       intr(EMM_INT,Regs);
  190.       { The number of EMS pages available is returned in BX     }
  191.       Pages_Available:=BX;
  192.       { The total number of pages present in the system is
  193.         returned in DX                                          }
  194.       Total_EMS_Pages:=DX;
  195.       { Return the error code                                   }
  196.       EMS_Pages_Available:=AH
  197.     end;
  198.   end; { EMS_Pages_Available }
  199.  
  200. { * --------------------------------------------------------- * }
  201.  
  202.   { This function requests the desired number of pages from the
  203.     EMM                                                         }
  204.   Function Allocate_Expanded_Memory_Pages
  205.     (Pages_Needed: Word; Var Handle: Word   ): Word;
  206.   Var
  207.     Regs: Registers;
  208.   Begin
  209.     with Regs do
  210.     Begin
  211.       { Put the desired EMS function number in the AH pseudo-
  212.         register                                                }
  213.       AH:= Allocate_Pages;
  214.       { Put the desired number of pages in BX                   }
  215.       BX:=Pages_Needed;
  216.       intr(EMM_INT,Regs);
  217.       { Our EMS handle is returned in DX                        }
  218.       Handle:=DX;
  219.       { Return the error code }
  220.       Allocate_Expanded_Memory_Pages:=AH;
  221.     end;
  222.   end; { Function Allocate_Expanded_Memory_Pages }
  223.  
  224. { * --------------------------------------------------------- * }
  225.  
  226.   { This function maps a logical page onto one of the physical
  227.     pages made available to us by the
  228.     Allocate_Expanded_Memory_Pages function                     }
  229.   Function Map_Expanded_Memory_Pages
  230.     (Handle,Logical_Page,Physical_Page: Word): Word;
  231.   Var
  232.     Regs: Registers;
  233.   Begin
  234.     with Regs do
  235.     Begin
  236.       { Put the desired EMS function number in the AH pseudo-
  237.         register                                                }
  238.       AH:=Map_Pages;
  239.       { Put the physical page number to be mapped into AL       }
  240.       AL:=Physical_Page;
  241.       { Put the logical page number to be mapped in    BX       }
  242.       BX:=Logical_Page;
  243.       { Put the EMS handle assigned to us earlier in   DX       }
  244.       DX:=Handle;
  245.       Intr(EMM_INT,Regs);
  246.       { Return the error code }
  247.       Map_Expanded_Memory_Pages:=AH;
  248.     end; { with Regs do }
  249.   end; { Function Map_Expanded_Memory_Pages }
  250.  
  251. { * --------------------------------------------------------- * }
  252.  
  253.   { This function gets the physical address of the EMS page
  254.     frame we are using. The address returned is the segment
  255.     of the page frame.                                          }
  256.   Function Get_Page_Frame_Base_Address
  257.     (Var Page_Frame_Address: Word): Word;
  258.   Var
  259.     Regs: Registers;
  260.   Begin
  261.     with Regs do
  262.     Begin
  263.       { Put the desired EMS function number in the AH pseudo-
  264.         register                                                }
  265.       AH:=Get_Page_Frame;
  266.       intr(EMM_INT,Regs);
  267.       { The page frame base address is returned in BX           }
  268.       Page_Frame_Address:=BX;
  269.       { Return the error code }
  270.       Get_Page_Frame_Base_Address:=AH;
  271.      end; { Regs }
  272.   end; { Function Get_Page_Frame_Base_Address }
  273.  
  274. { * --------------------------------------------------------- * }
  275.  
  276.   { This function releases the EMS memory pages allocated to
  277.     us, back to the EMS memory pool.                            }
  278.   Function Deallocate_Expanded_Memory_Pages
  279.     (Handle: Word): Word;
  280.   Var
  281.     Regs: Registers;
  282.   Begin
  283.     with Regs do
  284.     Begin
  285.       { Put the desired EMS function number in the AH pseudo-register }
  286.       AH:=DEALLOCATE_PAGES;
  287.       { Put the EMS handle assigned to our EMS memory pages in DX }
  288.       DX:=Handle;
  289.       Intr(EMM_INT,Regs);
  290.       { Return the error code }
  291.       Deallocate_Expanded_Memory_Pages:=AH;
  292.     end; { with Regs do }
  293.   end;  { Function Deallocate_Expanded_Memory_Pages }
  294.  
  295. { * --------------------------------------------------------- * }
  296.  
  297.   { This function returns the version number of the EMM as
  298.     a 3 character string.                                       }
  299.   Function Get_Version_Number(Var Version_String: ST3): Word;
  300.   Var
  301.     Regs: Registers;
  302.     Word_Part,Fractional_Part: Char;
  303.  
  304.   Begin
  305.     with Regs do
  306.     Begin
  307.       { Put the desired EMS function number in the AH pseudo-register }
  308.       AH:=GET_VERSION;
  309.       Intr(EMM_INT,Regs);
  310.       { See if call was successful }
  311.       If AH=STATUS_OK then
  312.       Begin
  313.         { The upper four bits of AH are the Word portion of the
  314.           version number, the lower four bits are the fractional
  315.           portion. Convert the Word value to ASCII by adding 48. }
  316.         Word_Part   := Char( AL shr 4 + 48);
  317.         Fractional_Part:= Char( AL and $F +48);
  318.         Version_String:= Word_Part+'.'+Fractional_Part;
  319.       end; { If AH=STATUS_OK }
  320.       { Return the function calls error code }
  321.       Get_Version_Number:=AH;
  322.     end; { with Regs do }
  323.   end; { Function Get_Version_Number }
  324. {--------------------}
  325. {SafeGuardMap}
  326. {Passes through the physical address array and adjusts it by removing
  327.  any entries which refer to entries in the 256K to 640K region since
  328.  these physical pages are reserved for operating system use and is likely
  329.  used by the current program as heap space.  (It is in conventional memory).}
  330. Procedure SafeGuardMap(var PageArr :PhysicalPageArr;
  331.                        var NumOPage:Word);
  332.  
  333. Var
  334.  
  335.      CurEntry:Byte;
  336.  
  337. {....................}
  338. {DeleteEntry}
  339. {Removes an entry from the physical address array, moving the other entries
  340.  up in the table.}
  341. Procedure DeleteEntry(    CurEntry:Byte;
  342.                       var PageArr :PhysicalPageArr;
  343.                       var NumOPage:Word);
  344.  
  345. Var
  346.  
  347.      WK:Byte;
  348.  
  349. BEGIN
  350.  
  351.   For WK:=CurEntry to NumOPage-1 do
  352.        PageArr[WK]:=PageArr[WK+1];
  353.   NumOPage:=NumOPage-1;
  354.  
  355. END;
  356.  
  357. BEGIN
  358.  
  359.   CurEntry:=0;
  360.   While (CurEntry<NumOPage) do
  361.        If (PageArr[CurEntry].PhysPageSegment<=$A000) then
  362.             DeleteEntry(CurEntry,PageArr,NumOPage)
  363.        Else
  364.             CurEntry:=CurEntry+1;
  365.  
  366. END;
  367. {--------------------}
  368. {GetPhysArr}
  369. {Gets the physical address array.}
  370. Function GetPhysArr(    Version :ST3;
  371.                     var PageArr :PhysicalPageArr;
  372.                     var NumOPage:Word;
  373.                     var PageBase:Word): Word;
  374. Var
  375.      Regs:Registers;
  376.      WK:Byte;
  377.  
  378. BEGIN
  379.  
  380.   If (Version[1]>='4') then
  381.        With Regs do
  382.          Begin
  383.            { Put the desired EMS function number in the AX pseudo-register }
  384.            AX:=GETMAPPHYADDARR;
  385.            {Put the address of the array in the appropriate registers.}
  386.            ES:=Seg(PageArr);
  387.            DI:=Ofs(PageArr);
  388.            Intr(EMM_INT,Regs);
  389.            { See if call was successful }
  390.            {CX contains the number of entries.}
  391.            If AH=STATUS_OK then
  392.               Begin
  393.                NumOPage:=CX;
  394.                SafeGuardMap(PageArr,NumOPage);
  395.               End;
  396.            { Return the function call's error code }
  397.            GetPhysArr:=AH;                                                    {Useless assignment}
  398.          End; { with Regs do }
  399.   If (Regs.AH<>STATUS_OK) or (Version[1]<'4') then
  400.      Begin
  401.        NumOPage:=4;
  402.        For WK:=0 to (NumOPage-1) do
  403.           begin
  404.             PhysicalPage[WK].PhysPageNumber:=WK;
  405.             PhysicalPage[WK].PhysPageSegment:=EMSPageBase+((WK*
  406.               SizeOPhysicalPage) shr 4);
  407.           end;
  408.        GetPhysArr:=STATUS_OK;
  409.      End;
  410.  
  411. END;
  412. { * --------------------------------------------------------- * }
  413.  
  414.   { This procedure prints an error message passed by the caller,
  415.     prints the error code passed by the caller in hex, and then
  416.     terminates the program with the an error level of 1         }
  417.  
  418.   Procedure Error(Error_Message: ST80; Error_Number: Word);
  419.   Begin
  420.     Writeln(Error_Message);
  421.     Writeln('  Error_Number = ',Hex_String(Error_Number) );
  422.     Writeln('Attempt to use EMS aborted.');
  423.     EMSUseful:=FALSE;
  424.   end; { Procedure Error_Message }
  425.  
  426. { * --------------------------------------------------------- * }
  427. {EMSInit}
  428. {Initializes the expanded memory system, allocating as many pages
  429.  as possible for the application.  Sets the flag EMSUseful if some
  430.  pages have been allocated.  Suppressed by having a command line parameter.}
  431. {Manipulates global variables: EMSUseful, and EMSTop}
  432. {OverRide turns EMS off.}
  433. Procedure EMSInit(OverRide:Boolean);
  434.  
  435. Var
  436.  
  437.      Regs               :Registers;
  438.      Error_Code         :Word;
  439.      Pages_EMS_Available:Word;
  440.      Total_EMS_Pages    :Word;
  441.      Version_Number     :ST3;
  442.      Pages_Number_String:ST3;
  443.      wk:word;
  444.  
  445. BEGIN
  446.  
  447.   ClrScr;
  448.   { Determine if the Expanded Memory Manager is installed, If
  449.     not, then terminate effort to use EMS. }
  450.   If (OverRide) then
  451.      Begin
  452.        WriteLn('Expanded memory will not be used.');
  453.        EMSUseful:=FALSE
  454.      End
  455.   else if not(Emm_Installed) then
  456.      Begin
  457.        Writeln('The LIM Expanded Memory Manager is not installed.');
  458.        EMSUseful:=FALSE
  459.      end
  460.   Else
  461.      Begin
  462.        EMSUseful:=TRUE;
  463.        { Get the version number and display it }
  464.        Error_Code:= Get_Version_Number(Version_Number);
  465.        If Error_Code<>STATUS_OK then
  466.          Error('Error trying to get the EMS version number ',
  467.                Error_code)
  468.        else
  469.          Writeln('LIM Expanded Memory Manager, version ',
  470.                  Version_Number,' is ready for use.');
  471.        Writeln;
  472.        { Determine if there are enough expanded memory pages for this
  473.          application. }
  474.        Error_Code:=
  475.          EMS_Pages_Available(Total_EMS_Pages,EMSPageAvail);
  476.        If Error_Code<>STATUS_OK then
  477.          Error('Error trying to determine the number of EMS pages available.',
  478.                Error_code)
  479.        Else
  480.           Begin
  481.             If (EMSPageAvail<=0) then
  482.                  EMSUseful:=FALSE;
  483.             Writeln('There are a total of ',Total_EMS_Pages,
  484.                     ' expanded memory pages present in this system.');
  485.             Writeln('  ',EMSPageAvail,
  486.                     ' of those pages are available for your usage.');
  487.             Writeln;
  488.           End;
  489.        { Allocate expanded memory pages for our usage }
  490.        End;
  491.   If EMSUseful then
  492.      Begin
  493.        Error_Code:= Allocate_Expanded_Memory_Pages(EMSPageAvail,EMSHandl);
  494.        Str(EMSPageAvail,Pages_Number_String);
  495.        If Error_Code<>STATUS_OK then
  496.          Error('EMS test program failed trying to allocate '+Pages_Number_String+
  497.                ' pages for usage.',Error_Code);
  498.        Writeln(EMSPageAvail,
  499.           ' EMS page(s) allocated.');
  500.        Writeln;
  501.      End;
  502.   If EMSUseful then
  503.      Begin
  504.        { Get the expanded memory page frame address }
  505.        Error_Code:= Get_Page_Frame_Base_Address(EMSPageBase);
  506.        If Error_Code<>STATUS_OK then
  507.          Error('EMS test program unable to get the base Page'+
  508.                ' Frame Address.',Error_Code)
  509.        Else
  510.           Begin
  511.             Writeln('The base address of the EMS page frame is - '+
  512.                     Hex_String(EMSPageBase) );
  513.             Writeln;
  514.           End;
  515.      End;
  516.   {Set up the physical page map.}
  517.   If EMSUseful then
  518.      Begin
  519.        Error_Code:=GetPhysArr(Version_Number,PhysicalPage,
  520.          NumOPhysicalPage,EMSPageBase);
  521.        WriteLn(NumOPhysicalPage,' physical pages.');
  522.        For WK:=0 to (NumOPhysicalPage-1) do
  523.             writeln(wk,' ',Hex_String(PhysicalPage[wk].PhysPageSegment),' ',
  524.               Hex_String(PhysicalPage[wk].PhysPageNumber));
  525.      End;
  526.   {Test for hardware/software implementation.}
  527.   If EMSUseful then
  528.      Begin
  529.        Error_Code:=Map_Expanded_Memory_Pages(EMSHandl,0,0);
  530.        Error_Code:=Map_Expanded_Memory_Pages(EMSHandl,0,1);
  531.        Byte(Ptr(EMSPageBase,0)^):=0;
  532.        Byte(Ptr(EMSPageBase+$0400,0)^):=0;
  533.        Byte(Ptr(EMSPageBase,0)^):=1;
  534.        Write  ('EMS implementation is likely in ');
  535.        If (Byte(Ptr(EMSPageBase+$0400,0)^)=1) then
  536.             WriteLn('HARDWARE.')
  537.        Else
  538.             WriteLn('SOFTWARE.')
  539.      End;
  540.   If not(EMSUseful) then
  541.        EMSPageAvail:=0;
  542.   Delay(3000);
  543.  
  544. END;
  545. {****************************************************************************}
  546. end.
  547.