meminfo.c
上传用户:xiaoan1112
上传日期:2013-04-11
资源大小:19621k
文件大小:82k
源码类别:

操作系统开发

开发平台:

Visual C++

  1. /*********************************************************************
  2.  * Microsoft Diagnostics Version 2.0
  3.  *
  4.  * A diagnostic utility to detect as much useful information about a
  5.  *   customer's computer system as is possible.
  6.  *
  7.  * Microsoft Diagnostics:  We detect the World.
  8.  *
  9.  * MEMINFO.C - Source file for obtaining memory information.
  10.  ********************************************************************/
  11. /* Include Files */
  12. #include "msd.h"
  13. /*********************************************************************
  14.  * GetMemInfo - Gets the memory information.
  15.  *
  16.  * pMem         - Pointer to memory information structure.
  17.  * fMinimumInfo - TRUE if minimum info is requested.
  18.  *
  19.  * Returns:  TRUE if an error occured.
  20.  *********************************************************************/
  21. BOOL GetMemInfo (MEMORY_STRUCT *pMem, BOOL fMinimumInfo)
  22. {
  23.   BOOL fReturnValue = FALSE;  /* Return value from various GetInfo calls */
  24.   WORD wSize;                 /* Size, in bytes, to store the data       */
  25.   COMPUTER_STRUCT *pComputer = NULL;  /* Computer info structure pointer */
  26.   WORD wProcessor;            /* Stores the processor type               */
  27.   WORD wBusType;              /* Stores the computer's bus type          */
  28.   wSize = GetInfoSize (IDI_COMPUTER_RECORD, FALSE);
  29.   if (wSize == 0)
  30.     return (TRUE);
  31.   pComputer = malloc (wSize);
  32.   if (pComputer == NULL)
  33.     {
  34.       OutOfMemory();
  35.       return (TRUE);
  36.     }
  37.   /* Zero out the structure */
  38.   memset (pComputer, '', wSize);
  39.   fReturnValue = GetComputerIrqInfo (pComputer);
  40.   if (fReturnValue)
  41.     {
  42.       free (pComputer);
  43.       return (fReturnValue);
  44.     }
  45.   wProcessor = pComputer->wProcessor;
  46.   wBusType   = pComputer->wBusType;
  47.   free (pComputer);
  48.   /* Initialize the memory info structures */
  49.   MInitialize ((MEMORY_STRUCT FAR *) pMem);
  50.   /* Get conventional memory size */
  51.   Get_Conv_Mem_Info ((MEMORY_STRUCT FAR *) pMem);
  52.   if (wProcessor >= _80286)
  53.     {
  54.       /* Get installed extended according to CMOS */
  55.       GetCMOSExtended ((MEMORY_STRUCT FAR *) pMem);
  56.       /* Get non-XMS extended memory size */
  57.       Get_Ext_Mem_Info ((MEMORY_STRUCT FAR *) pMem);
  58.       /* Get XMS extended memory info */
  59.       Get_Xmm_Info ((MEMORY_STRUCT FAR *) pMem, wProcessor);
  60.       /* Make sure we're on at least a 386 for DPMI information */
  61.       if (wProcessor != _80286)
  62.         GetDPMIInfo ((MEMORY_STRUCT FAR *) pMem);
  63.     }
  64.   /* Check for an EMM driver */
  65.   Test_For_Emm ((MEMORY_STRUCT FAR *) pMem);
  66.   if (pMem->iEmm_Is_There)
  67.     {
  68.       /* Get expanded memory info */
  69.       Get_Emm_Info ((MEMORY_STRUCT FAR *) pMem);
  70.       /* If on a 386 or greater */
  71.       if (/* !(pMem->iDPMIPresent) && */ wProcessor >= _80386)
  72.         {
  73.           /* check for and get VCPI info */
  74.           GetVCPIInfo ((MEMORY_STRUCT FAR *) pMem);
  75.         }
  76.     }
  77.   if (fMinimumInfo)
  78.     return (FALSE);
  79.   /* Get the visual memory map */
  80.   Get_VisualMemMap ((MEMORY_STRUCT FAR *) pMem, wBusType);
  81.   /* Fill the visual memory map overlay with EMS page frame and UMBs */
  82.   VisualMapOverlay ((MEMORY_STRUCT FAR *) pMem);
  83.   return (FALSE);
  84. }
  85. /************************************************************************
  86. *
  87. * Function MInitialize()
  88. *
  89. * This function initializes fields in the Mem_Info_St structure to
  90. * zero values.
  91. *
  92. ************************************************************************/
  93. int MInitialize (MEMORY_STRUCT FAR * pMem_Info)
  94. {
  95.   /* Initialize the fields in the Mem_Info_St structure */
  96.   _fmemset (pMem_Info, '', sizeof (MEMORY_STRUCT));
  97.   return (0);
  98. }
  99. /***************************************************************************
  100. *
  101. * Function Get_Conv_Mem_Info()
  102. *
  103. * This function gets the amount of conventional memory in bytes and the
  104. * amount of free conventional memory in bytes. It uses a call to BIOS
  105. * function Int 12H.                                                                    *
  106. *
  107. *
  108. * Mem_Info_St Fields Set
  109. * ----------------------
  110. *
  111. * lConv_Mem     = The amount of conventional memory in bytes
  112. * lFree_Conv_Mem  = The amount of free conventional memory in bytes
  113. *
  114. *
  115. * Local Variables Used                                      
  116.     *
  117. * --------------------
  118. *
  119. *
  120. * inregs, outregs : Used for reading and writing the general purpose
  121. *         registers.
  122. * segment_prefix  : Used to hold the segment address of the program
  123. *         segment prefix.
  124. *
  125. ***************************************************************************/
  126. int Get_Conv_Mem_Info (MEMORY_STRUCT FAR * pMem_Info)
  127. {
  128.   union REGS inregs, outregs;
  129.   long segment_prefix;
  130.   int86(0x12, &inregs, &outregs);
  131.   pMem_Info->lConv_Mem = outregs.x.ax * 1024L;
  132.   segment_prefix = 0L;
  133.   segment_prefix = segment_prefix + ((long)(_psp) * 0x10);
  134.   pMem_Info->lFree_Conv_Mem = pMem_Info->lConv_Mem - segment_prefix;
  135.   return (0);
  136. }
  137. /***************************************************************************
  138. *
  139. * Function Get_Ext_Mem_Info()
  140. *
  141. * This function gets the amount of extended memory in bytes. It uses a
  142. * call to BIOS function Int 15H, subfunction 88H.
  143. *
  144. *
  145. * Mem_Info_St Fields Set
  146. * ----------------------
  147. *
  148. * lExt_Mem      = The amount of extended memory in bytes
  149. *
  150. *
  151. * Local Variables Used
  152. * --------------------
  153. *
  154. * inregs, outregs : Used for reading and writing the general purpose
  155. *         registers.
  156. *
  157. ***************************************************************************/
  158. int Get_Ext_Mem_Info (MEMORY_STRUCT FAR * pMem_Info)
  159. {
  160.   union REGS inregs, outregs;
  161.   static int iFirstTime = 1;
  162.   /* Check to see if this is the first time this routine was called.
  163.      We do this because of the XMS memory routine which is called
  164.      after this. In the XMS memory routine, if an XMS driver is found,
  165.      the driver hooks into INT 15 calls. Subsequent calls to INT 15
  166.      to get the amount of extended memory then return 0K. By calling
  167.      this routine first and then never checking it again, we are able
  168.      to get a valid value for extended memory at least the first time
  169.      we are run on the machine */
  170.   if (iFirstTime)
  171.   {
  172.     inregs.h.ah = 0x88;
  173.     int86(0x15, &inregs, &outregs);
  174.     pMem_Info->iExt_Mem = outregs.x.ax;
  175.     iFirstTime = 0;
  176.   }
  177.   return (0);
  178. }
  179. /***************************************************************************
  180. *
  181. * Function GetCMOSExtended ()
  182. *
  183. * This function gets the amount of installed extended memory according to
  184. * the CMOS.
  185. *
  186. ***************************************************************************/
  187. int GetCMOSExtended (MEMORY_STRUCT FAR * pMem_Info)
  188. {
  189.   int iHighByte, iLowByte, iTotalMemory;
  190.   int iLoop;
  191.   outp (0x70, 0x17);
  192.   for (iLoop = 0; iLoop < 100; iLoop++)
  193.     ;
  194.   iLowByte = inp (0x71);
  195.   outp (0x70, 0x18);
  196.   for (iLoop = 0; iLoop < 100; iLoop++)
  197.     ;
  198.   iHighByte = inp (0x71);
  199.   iTotalMemory = (iHighByte << 8) + iLowByte;
  200.   pMem_Info->iCMOSExtended = iTotalMemory;
  201.   return (0);
  202. }
  203. /***************************************************************************
  204. *
  205. * Function Test_For_Emm()
  206. *
  207. * This function tests for the existence of an EMM driver. It makes a call
  208. * to Int 21H Function 35H to get the address of the interrupt handler for
  209. * the EMM driver.  It then looks at offset 0AH of the device header to
  210. * check for the guaranteed name of the EMM driver, EMMXXXX0. If the
  211. * guaranteed name is found, there is an EMM driver installed and the value
  212. * True is put in the iEmm_Is_There field of the Mem_Info_St structure.
  213. * If the guaranteed name is not found, a value of False is put in the
  214. * iEmm_Is_There field of the Mem_Info_St structure.
  215. *
  216. *
  217. * Mem_Info_St Fields Set
  218. * ----------------------
  219. *
  220. * iEmm_IS_There  = 1 if an EMM driver is found, 0 if not
  221. *
  222. *
  223. * Local Variables Used
  224. * --------------------
  225. *
  226. * inregs, outregs:  For loading and reading the general purpose regs.
  227. * segments     :  For loading and reading the segment regs.
  228. * addr_of_emm  :  The base address of the EMM driver, if it is there.
  229. * ptr_to_emm   :  A pointer to the base address of the EMM driver
  230. * name_field   :  An array to hold the string found at the name field of
  231. *         device header of the EMM driver.
  232. * test_name    :  An array to hold the guaranteed name of the EMM
  233. *         driver, EMMXXXX0.
  234. * index      :  Used for loop control.
  235. * True, False  :  Used for Boolean testing.
  236. *
  237. ***************************************************************************/
  238. int Test_For_Emm (MEMORY_STRUCT FAR * pMem_Info)
  239. {
  240.   union REGS inregs, outregs;
  241.   struct SREGS segments;
  242.   unsigned long addr_of_emm;
  243.   char far *ptr_to_emm = NULL;
  244.   char name_field[9];
  245.   char test_name[9] = {
  246.         'E', 'M', 'M', 'X',
  247.         'X', 'X', 'X', '0', ''
  248.   };
  249.   int index;
  250.   int True = 1;
  251.   int False = 0;
  252.   /* load AL with the EMM int vector of 67H and prepare
  253.      to call Int 21H, Function 35H */
  254.   inregs.h.al = 0x67;
  255.   inregs.h.ah = 0x35;
  256.   /* After this call, the ES reg will contain the EMM handler address
  257.      if it is there */
  258.   int86x(0x21, &inregs, &outregs, &segments);
  259.   segread(&segments);
  260.   addr_of_emm = 0L;
  261.   /* Load addr_of_emm with address in the ES reg, then shift it left
  262.      16 bits to get it into the high order word */
  263.   addr_of_emm = (addr_of_emm | segments.es) <<16;
  264.   /* Add the offset of 0AH to make ptr_to_emm point to the name field
  265.      in the device header of the EMM driver */
  266.   ptr_to_emm = (unsigned char far *)addr_of_emm + 0x0A;
  267.   /* Load the the first 8 characters beginning at the address pointed
  268.      to by the pointer ptr_to_emm, then add a terminating  */
  269.   for(index = 0; index < 8; ++index)
  270.   {
  271.     name_field[index] = *ptr_to_emm;
  272.          ++ptr_to_emm;
  273.   }
  274.   name_field[index] = '';
  275.   /* Compare the characters loaded into name_field[] against the
  276.      guaranteed EMM driver name stored in test_name[]. Set
  277.      iEmm_Is_There to True if equal, False if not */
  278.   if (strcmp(test_name, name_field))
  279.     pMem_Info->iEmm_Is_There = False;
  280.   else
  281.     pMem_Info->iEmm_Is_There = True;
  282.   return (0);
  283. }
  284. /**************************************************************************
  285. *
  286. * Function Get_Emm_Info()
  287. *
  288. * This function gets the EMM driver version, the total number of 16K EMM
  289. * pages and the number of 16K EM pages currently available.
  290. *
  291. *
  292. * Mem_Info_St Fields Set
  293. * ----------------------
  294. *
  295. * iEmm_VersionMajor = The EMS version supported by the EMS driver
  296. * iEmm_VersionMinor = The EMS version supported by the EMS driver
  297. * iTotal_Emm_Pages  = The total number of logical expanded memory pages
  298. *                     present
  299. * iFree_Emm_Pages   = The number of logical pages not allocated
  300. * iEmm_Ver_Err      = Error code returned while getting the version
  301. * iEmm_Size_Err     = Error code returned while getting allocation info
  302. *
  303. *
  304. * Local Variables Used
  305. * --------------------
  306. *
  307. * inregs, outregs  : Used to load and read the general purpose
  308. *            registers
  309. * int_portion    : Used to figure the integer portion of the version
  310. * frac_portion     : Used to figure the fractional portion of the
  311. *            version
  312. * uiPageFrameAddress : Used to get the EMS page frame address
  313. * uiMappablePages  : Used to get the number of LIM 4.0 mappable pages
  314. *
  315. **************************************************************************/
  316. int Get_Emm_Info(MEMORY_STRUCT FAR * pMem_Info)
  317. {
  318.   union REGS inregs, outregs;
  319.   struct SREGS segments;
  320.   int int_portion, frac_portion;
  321.   unsigned int uiPageFrameAddress;
  322.   unsigned int *pPtrToConfigBuffer = NULL;
  323.   /* Get the EMM version */
  324.   inregs.h.ah = 0x46;
  325.   int86(0x67, &inregs, &outregs);
  326.   if (outregs.h.ah == 0x0)
  327.   {
  328.     frac_portion = (outregs.h.al & 0x0F);
  329.     int_portion = ((outregs.h.al >> 4) & 0x0F);
  330.     pMem_Info->iEmm_VersionMajor = int_portion;
  331.     pMem_Info->iEmm_VersionMinor = frac_portion;
  332.   }
  333.   else
  334.     pMem_Info->iEmm_Ver_Err = outregs.h.ah;
  335.   /* Get the total number of EM pages and the number free using a
  336.      LIM 3.X call */
  337.   inregs.h.ah = 0x42;
  338.   int86(0x67, &inregs, &outregs);
  339.   if (outregs.h.ah)
  340.     pMem_Info->iEmm_Size_Err = outregs.h.ah;
  341.   else
  342.   {
  343.     pMem_Info->iTotal_Emm_Pages = outregs.x.dx;
  344.     pMem_Info->iFree_Emm_Pages = outregs.x.bx;
  345.   }
  346.   /* If LIM 4.0 was detected use a LIM 4.0 specific call to get the
  347.      total number of raw EM pages and the number of raw EM pages
  348.      available */
  349.   if (pMem_Info->iEmm_VersionMajor == 4 && pMem_Info->iEmm_VersionMinor == 0)
  350.     {
  351.       /* Get the size of a raw EM page from the EM hardware configuration */
  352.       if (( pPtrToConfigBuffer = (unsigned int *) malloc (10)) == NULL )
  353.         pMem_Info->iEmm_Size_Err = outregs.h.ah; /* The malloc failed */
  354.       else
  355.         {
  356.           /* Get the EM hardware configuration */
  357.           inregs.x.ax = 0x5900;
  358.           segread (&segments);
  359.           segments.es = (unsigned int) FP_SEG (pPtrToConfigBuffer);
  360.           inregs.x.di = (unsigned int) FP_OFF (pPtrToConfigBuffer);
  361.           int86x (0x67, &inregs, &outregs, &segments);
  362.           if (outregs.h.ah) /* There was an error getting the EM config */
  363.             pMem_Info->iEmm_Size_Err = outregs.h.ah;
  364.           else
  365.             {
  366.               /* At this point pPtrToConfigBuffer is pointing to an integer
  367.                  value which is the raw EM page size, in paragraphs. Next,
  368.                  get the total number of raw pages and the number available */
  369.               inregs.x.ax = 0x5901;
  370.               int86 (0x67, &inregs, &outregs);
  371.               if (outregs.h.ah) /* There was an error returned */
  372.                 pMem_Info->iEmm_Size_Err = outregs.h.ah;
  373.               else
  374.                 {
  375.                   pMem_Info->iTotal_Emm_Pages = outregs.x.dx;
  376.                   pMem_Info->iFree_Emm_Pages = outregs.x.bx;
  377.                 }
  378.             }
  379.         }
  380.     }
  381.   /* Free up the EMS pointer */
  382.   free (pPtrToConfigBuffer);
  383.   /* Get the EMS page frame address */
  384.   inregs.h.ah = 0x41;
  385.   int86(0x67, &inregs, &outregs);
  386.   uiPageFrameAddress = outregs.x.bx;
  387.   if (!outregs.h.ah) /* A page frame address was returned */
  388.     pMem_Info->iPageFrameAddress = uiPageFrameAddress;
  389.   return (0);
  390. }
  391. /************************************************************************
  392. *
  393. * Function GetVCPIInfo ()
  394. *
  395. * This function detects whether or not a VCPI control program is present.
  396. * If one is, information about the amount of VCPI memory available is
  397. * gathered.
  398. *
  399. ************************************************************************/
  400. int GetVCPIInfo (MEMORY_STRUCT FAR * pMem_Info)
  401. {
  402.   union REGS inregs, outregs;
  403.   struct SREGS segments;
  404.   inregs.x.ax = 0xDE00;
  405.   int86x (0x67, &inregs, &outregs, &segments);
  406.   if( outregs.h.ah == 0x00 )
  407.   {
  408.     pMem_Info->iVCPIPresent = 1;
  409.     pMem_Info->iVCPIMajorVersion = outregs.h.bh;
  410.     pMem_Info->iVCPIMinorVersion = outregs.h.bl;
  411.     /* Query the amount of available VCPI memory */
  412.     inregs.x.ax = 0xDE03;
  413.     int86x (0x67, &inregs, &outregs, &segments);
  414.     if (outregs.h.ah == 0x00 )
  415.     pMem_Info->iVCPIAvailMemory = outregs.x.dx * 4;
  416.   }
  417.   return (0);
  418. }
  419. /************************************************************************
  420. *
  421. * Function GetDPMIInfo ()
  422. *
  423. * This function detects whether or not a DPMI control program is present.
  424. * If one is, information about the amount of DPMI memory available is
  425. * gathered by calling the function QueryDPMIMemInfo ().
  426. *
  427. ************************************************************************/
  428. int GetDPMIInfo (MEMORY_STRUCT FAR * pMem_Info)
  429. {
  430.   union REGS inregs, outregs;
  431.   struct SREGS segments;
  432.   inregs.x.ax = 0x1687;
  433.   int86x (0x2F, &inregs, &outregs, &segments);
  434.   if (outregs.x.ax == 0x00)
  435.   {
  436.     pMem_Info->iDPMIPresent = 1;
  437.     pMem_Info->iDPMIMajorVersion = outregs.h.dh;
  438.     pMem_Info->iDPMIMinorVersion = outregs.h.dl;
  439.     /* Query the DPMI control program */
  440. /*
  441.     QueryDPMIMemInfo (pMem_Info);
  442. */
  443.   }
  444.   return (0);
  445. }
  446. /************************************************************************
  447. *
  448. * Function QueryDPMIMemInfo ()
  449. *
  450. * This function queries the DPMI control program for amounts of DPMI
  451. * memory.
  452. *
  453. ************************************************************************/
  454. int QueryDPMIMemInfo (MEMORY_STRUCT FAR * pMem_Info)
  455. {
  456.   long DPMIControl;
  457.   unsigned short wRMMemAvail;
  458.   DPMI_STRUCT FAR * pDPMIMemInfo = NULL;
  459.   pDPMIMemInfo = (DPMI_STRUCT FAR *) &(pMem_Info->DPMIMemInfo);
  460.   _asm
  461.   {
  462.     ; Get the entry point of the DPMI control program
  463.     ; and save it
  464.     mov   ax, 1687h
  465.     int   2Fh
  466.     test  ax, ax
  467.     jnz   Failure
  468.     mov   word ptr [DPMIControl],di
  469.     mov   word ptr [DPMIControl+2],es
  470.     ; if si != 0 then we need to allocate some memory
  471.     ; for the dos extenders private data
  472.     test  si, si
  473.     jz    SiZero
  474.     mov   bx, si
  475.     mov   ah, 48h
  476.     int   21h
  477.     jc    Failure
  478.     mov   es, ax
  479. SiZero:
  480.     xor   ax,ax
  481.     call  [DPMIControl]
  482.     jc    Failure
  483.     les   di, pDPMIMemInfo
  484.     mov   ax, 0500h
  485.     int   31h
  486.     jc    Failure
  487.     mov   bx, 0ffffh
  488.     mov   ax, 0100h
  489.     int   31h
  490.     mov   wRMMemAvail, bx
  491. }
  492.   pMem_Info->ulRealModeDPMIMemAvail = wRMMemAvail * 16;
  493. Failure:
  494.   return (0);
  495. }
  496. /************************************************************************
  497. *
  498. * Function Get_Xmm_Info()
  499. *
  500. * This function first tests for the existence of an XMS driver.  If an
  501. * XMS driver is present, it returns the version of the XMS driver, the
  502. * version of the XMS specification being used, whether or not the high
  503. * memory area (HMA) is present, and whether or not the HMA is available.
  504. * It also returns the size of the largest free extended memory block in
  505. * bytes, and the total amount of free extended memory in bytes.
  506. *
  507. *
  508. * Mem_Info_St Fields Set
  509. * ----------------------
  510. *
  511. * iXmm_Is_There            = 1 if an XMS driver is present, 0 if not
  512. * iXmm_Spec_VersionMajor   = The major version of the XMS specification
  513. * iXmm_Spec_VersionMinor   = The minor version of the XMS specification
  514. * iXmm_Driver_VersionMajor = The internal major version of the XMM driver
  515. * iXmm_Driver_VersionMinor = The internal minor version of the XMM driver
  516. * lLargest_Free_Xm         = The size in bytes of the largest free block of
  517. *                            extended memory. Includes the 64K HMA if it is
  518. *                            available.
  519. * lTotal_Free_XM           = The size in bytes of the total amount of free
  520. *                            extended memory. Includes the 64K HMA if it is
  521. *                            available.
  522. *
  523. * Local Variables Used
  524. * --------------------
  525. *
  526. * XMSControl       : The address of the XMS driver control function
  527. * XMSIsThere       : Used to flag whether or not an XMS driver is
  528. *                    present
  529. * XMSSpecVersion   : The BCD version of the XMS spec being used
  530. * XMSDriverVersion : The BCD version of the XMS driver being used
  531. * LargestFreeBlock : Used to calculate the largest free block
  532. * TotalFreeBlocks  : Used to calculate the total amount free
  533. * HMAStatus        : Used to check the availability of the HMA
  534. * XMSError         : Used to hold error codes returned from the XMS
  535. *                    calls
  536. * A20Status        : Used to hold the status of the A20 address line
  537. * True, False      : Used to set value of iXmm_Is_There
  538. *
  539. ************************************************************************/
  540. int Get_Xmm_Info(MEMORY_STRUCT FAR * pMem_Info, WORD wProcessorType)
  541. {
  542.   long XMSControl;
  543.   unsigned char XMSIsThere;
  544.   unsigned char XMSSpecVersionMajor, XMSSpecVersionMinor;
  545.   unsigned char XMSDriverVersionMajor, XMSDriverVersionMinor;
  546.   unsigned int  LargestFreeBlock, TotalFreeBlocks;
  547.   unsigned int  HMAStatus;
  548.   unsigned char XMSError;
  549.   unsigned int  A20Status;
  550.   int True = 1;
  551.   int False = 0;
  552.   int Available = 1;
  553.   int NotAvailable = 0;
  554.   DWORD SxmsLargestFree;  /* Largest free SXMS block */
  555.   DWORD SxmsTotalFree;    /* Total free SXMS         */
  556.   /* Determine if an XMS driver is there */
  557.   
  558.   _asm
  559.   {
  560.     /* After setting AX=43h, AL=00h and calling INT 2Fh, if an
  561.        XMS driver is present, AL will be set to 80h */
  562.     mov   ax,4300h
  563.     int   2Fh
  564.     mov   XMSIsThere,al
  565.   }
  566.   if ( XMSIsThere == 0x80 ) /* There is an XMS driver present */
  567.   {
  568.     pMem_Info->iXmm_Is_There = True;
  569.     
  570.     _asm
  571.     {
  572.       /* Get the address of the XMS driver's control function.
  573.          Once we have that we can make calls to it to get the
  574.          other info we want */
  575.       mov   ax,4310h
  576.       int   2Fh
  577.       mov   word ptr [XMSControl],bx
  578.       mov   word ptr [XMSControl+2],es
  579.       /* Get the XMS spec and driver version numbers, in BCD */
  580.       mov   ah,00h
  581.       call  [XMSControl]
  582.       mov   XMSSpecVersionMajor,ah
  583.       mov   XMSSpecVersionMinor,al
  584.       mov   XMSDriverVersionMajor,bh
  585.       mov   XMSDriverVersionMinor,bl
  586.     }
  587.     /* Get the XMS specification version */
  588.     pMem_Info->uchXmm_Spec_VersionMajor = XMSSpecVersionMajor;
  589.     pMem_Info->uchXmm_Spec_VersionMinor = XMSSpecVersionMinor;
  590.     /* Get the XMS driver version */
  591.     pMem_Info->uchXmm_Driver_VersionMajor = XMSDriverVersionMajor;
  592.     pMem_Info->uchXmm_Driver_VersionMinor = XMSDriverVersionMinor;
  593.     /* Query the amount of free extended memory */
  594.     
  595.     _asm
  596.     {
  597.       mov   ah,08h
  598.       call  [XMSControl]
  599.       mov   LargestFreeBlock,ax
  600.       mov   TotalFreeBlocks,dx
  601.       mov   XMSError,bl
  602.     }
  603.     /* Store the largest free block and the total free blocks */
  604.     pMem_Info->iLargest_Free_Xm = LargestFreeBlock;
  605.     pMem_Info->iTotal_Free_Xm = TotalFreeBlocks;
  606.     /* Check to see if the HMA is available by issuing a request for it */
  607.     
  608.     _asm
  609.     {
  610.       /* Return the status of the request in HMAStatus. 0x0001 means
  611.          the request succeeded, 0x0000 means it failed */
  612.       mov   ah,01h
  613.       call  [XMSControl]
  614.       mov   HMAStatus,ax
  615.       mov   XMSError,bl
  616.     }
  617.     if (HMAStatus) /* The HMA request succeeded */
  618.     {
  619.       pMem_Info->iHMAStatus = Available;
  620.       /* Release the HMA */
  621.       _asm
  622.       {
  623.         /* Return the status of the release in HMAStatus. 0x0001 means
  624.            the HMA was released successfully, 0x0000 means otherwise */
  625.         mov   ah,02h
  626.         call  [XMSControl]
  627.         mov   HMAStatus,ax
  628.         mov   XMSError,bl
  629.       }
  630.       if (!HMAStatus) /* The HMA was not successfully released */
  631.         pMem_Info->iXMSError = 6; /* There was an error releasing the HMA */
  632.     }
  633.     else /* the HMA request failed */
  634.     {
  635.       pMem_Info->iHMAStatus = NotAvailable;
  636.       switch (XMSError) /* Get the reason why the HMA request failed */
  637.       {
  638.         case 0x80: pMem_Info->iXMSError = 1; /* Feature not implemented */
  639.                    break;
  640.         case 0x81: pMem_Info->iXMSError = 2; /* VDISK was detected */
  641.                    break;
  642.         case 0x90: pMem_Info->iXMSError = 3; /* HMA does not exist */
  643.                    break;
  644.         case 0x91: pMem_Info->iXMSError = 4; /* HMA is already in use */
  645.                    break;
  646.           default: pMem_Info->iXMSError = 5;   /* Unknown reason */
  647.       }
  648.     }
  649.     /* Query the status of the A20 address line */
  650.     
  651.     _asm
  652.     {
  653.       mov   ah,07h
  654.       call  [XMSControl]
  655.       mov   A20Status,ax
  656.       mov   XMSError,bl
  657.     }
  658.     /* Store the A20 line status */
  659.     pMem_Info->iA20Status = A20Status;
  660.     /* Detect the XMS UMB free areas */
  661. #define XMS_UMB_ERROR       2
  662. #define MAX_XMS_UMB_SEGS  100
  663.     {
  664.       BYTE  bErrorCode;         /* Error code from XMS UMB call         */
  665.       WORD  wSegmentAddr;       /* Segment address of free XMS UMB      */
  666.       WORD  wSegmentSize;       /* Size of XMS UMB                      */
  667.       WORD wTotalFree = 0;      /* Total amount of free XMS UMB space   */
  668.       WORD wLargestFree = 0;    /* Largest free area                    */
  669.                                 /* Segment addresses of XMS UMBs        */
  670.       WORD  wSegments[MAX_XMS_UMB_SEGS + 1];
  671.       WORD  i;                  /* Looping variable                     */
  672.       BOOL  fDoneFlag = FALSE;  /* TRUE when all XMS UMBs accounted for */
  673.       int iRow, iCol;             /* Row and Column of visual map overlay   */
  674.       int iNumOfChars;            /* Number of characters (or 1K blocks)    */
  675.       
  676.       for (i = 0; i < MAX_XMS_UMB_SEGS && fDoneFlag == FALSE; ++i)
  677.         {
  678.           _asm
  679.             {
  680.               mov   ah, 0x10            ; Request XMS UMB
  681.               mov   dx, 0xFFFF          ; Requested size in paragraphs
  682.               call  [XMSControl]        ; Make the call
  683.               or    ax,ax               ; AX == 0x0000 if it failed
  684.               jz    FirstErrResults     ; Jump around if it failed
  685.               mov   fDoneFlag, XMS_UMB_ERROR ; There is no way this could
  686.                                         ;   have succeeded.  If it did,
  687.                                         ;   bomb out immediately.
  688.               jmp   FirstXmsUmbDone
  689.             FirstErrResults:
  690.               mov   bErrorCode, bl      ; Store the error code
  691.               mov   wSegmentSize, dx    ; Store size of largest free block
  692.             FirstXmsUmbDone:
  693.             }
  694.           /* Bomb out if 0xFFFF succeeded */
  695.           if (fDoneFlag == XMS_UMB_ERROR)
  696.             break;
  697.           /* Are there no more segments free */
  698.           if (bErrorCode == 0xB1)
  699.             fDoneFlag == TRUE;
  700.           /* If the function was not implemented, bomb out */
  701.           if (bErrorCode == 0x80)
  702.             {
  703.               pMem_Info->fXmsUmbAvailable = FALSE;
  704.               fDoneFlag = XMS_UMB_ERROR;
  705.               break;
  706.             }
  707.           /* If an unexpected error occured, bomb out */
  708.           if (bErrorCode != 0xB0)
  709.             {
  710.               fDoneFlag = XMS_UMB_ERROR;
  711.               break;
  712.             }
  713.           /* The XMS UMB calls are available */
  714.           pMem_Info->fXmsUmbAvailable = TRUE;
  715.           /* One segment of wSegmentSize size is free -- allocate it */
  716.           _asm
  717.             {
  718.               mov   ah, 0x10            ; Request XMS UMB
  719.               mov   dx, wSegmentSize    ; Requested size in paragraphs
  720.               call  [XMSControl]        ; Make the call
  721.               or    ax,ax               ; AX == 0x0000 if it failed
  722.               jz    SecondErrResults    ; Jump around if it failed
  723.               mov   wSegmentAddr, bx    ; Store XMS UMB segment address
  724.               mov   wSegmentSize, dx    ; Store size of block
  725.               jmp   SecondXmsUmbDone
  726.             SecondErrResults:
  727.               mov   fDoneFlag, XMS_UMB_ERROR ; This should not have failed.
  728.                                         ;   If it did, bomb out
  729.                                         ;   immediately.
  730.             SecondXmsUmbDone:
  731.             }
  732.           /* Bomb out if 0xFFFF succeeded */
  733.           if (fDoneFlag == XMS_UMB_ERROR)
  734.             break;
  735.           /* Accumulate the results */
  736.           wSegments[i] = wSegmentAddr;
  737.           if (wSegmentSize > wLargestFree)
  738.             wLargestFree = wSegmentSize;
  739.           wTotalFree += wSegmentSize;
  740.           /* Fill the memory map with "X"es */
  741.           iRow        = wSegmentAddr / 0x400;
  742.           iCol        = (wSegmentAddr % 0x400) / 0x40;
  743.           iNumOfChars = (WORD) ((((DWORD) wSegmentSize << 4) + 1023) / 1024);
  744.           FillMemMapOverlay (pMem_Info, iRow, iCol, iNumOfChars,
  745.                              REPORT_FREE_XMS_UMB);
  746.         }
  747.       wSegments[i] = 0x0000;
  748.       /* Store the results in the XMS UMB area of pMem_Info */
  749.       pMem_Info->dwXmsUmbFree = (DWORD) wTotalFree << 4;
  750.       pMem_Info->dwXmsUmbLargestFree = (DWORD) wLargestFree << 4;
  751.       /* The amounts have been determined -- free the blocks */
  752.       for (i = 0; i < MAX_XMS_UMB_SEGS && wSegments[i] != 0x0000; ++i)
  753.         {
  754.           wSegmentAddr = wSegments[i];
  755.           _asm
  756.             {
  757.               mov   ah, 0x11            ; Release XMS UMB
  758.               mov   dx, wSegmentAddr    ; paragraph to release
  759.               call  [XMSControl]        ; Make the call
  760.               or    ax,ax               ; AX == 0x0000 if it failed
  761.               jnz   ThirdXmsUmbDone     ; Jump around if it failed
  762.               mov   fDoneFlag, XMS_UMB_ERROR ; This should not have failed.
  763.                                         ;   If it did, bomb out
  764.                                         ;   immediately.
  765.             ThirdXmsUmbDone:
  766.             }
  767.           if (fDoneFlag == XMS_UMB_ERROR)
  768.             break;
  769.         }
  770.     }
  771.     /* Get the Super Extended Memory values */
  772.     {
  773.       if (wProcessorType > _80286 &&
  774.           GetSuperXmsInfo (XMSControl, &SxmsLargestFree, &SxmsTotalFree) == 0)
  775.         {
  776.           /* Super XMS exists */
  777.           pMem_Info->fSxmsAvailable    = TRUE;
  778.           pMem_Info->dwSxmsLargestFree = SxmsLargestFree;
  779.           pMem_Info->dwSxmsTotalFree   = SxmsTotalFree;
  780.         }
  781.       else
  782.         {
  783.           /* There is no Super XMS */
  784.           pMem_Info->fSxmsAvailable = FALSE;
  785.         }
  786.     }
  787.   }
  788.   else /* There is no XMS driver present */
  789.     pMem_Info->iXmm_Is_There = False;
  790.   return (0);
  791. }
  792. /************************************************************************
  793.  * Function GetRomMap - Fills a ROM_MAP structure with the information
  794.  *                      about ROM BIOSes and Option ROMs in the system.
  795.  *
  796.  * pRomMap - Pointer to the structure for storing the ROM map.
  797.  ***********************************************************************/
  798. VOID GetRomMap (ROM_MAP *pRomMap, WORD wBusType)
  799. {
  800.   int  iMapSegment, iMapOffset;
  801.   WORD wOptIndex = 0;   /* Option ROM index */
  802.   unsigned uSegment;
  803.   unsigned uOffset;
  804.   int iValidRom;
  805.   int iFalse = 0;
  806.   int iTrue = 1;
  807.   unsigned END_SEGMENT = 0xEC00;
  808.   /* Check if we are on a ps/2 or not. If this is a ps/2, the
  809.      range E000-EFFF is system ROM so we only need to check up
  810.      through the DC00 page.  If this is not a ps/2, the range
  811.      F000-EFFF is ROM so we only need to check up through page EC00 */
  812.   if (wBusType == 3)         /* Is this a PS/2? (MCA) */
  813.     {
  814.       END_SEGMENT = 0xDC00;
  815.       pRomMap->wRomBiosLoc[0]   = 0xE000;
  816.       pRomMap->dwRomBiosSize[0] = 0x10000;
  817.       pRomMap->wRomBiosLoc[1]   = 0xF000;
  818.       pRomMap->dwRomBiosSize[1] = 0x10000;
  819.     }
  820.   else
  821.     {
  822.       pRomMap->wRomBiosLoc[0]   = 0xF000;
  823.       pRomMap->dwRomBiosSize[0] = 0x10000;
  824.       pRomMap->wRomBiosLoc[1]   = 0;
  825.       pRomMap->dwRomBiosSize[1] = 0;
  826.     }
  827.   /* Set the indexes for the memory search to point to just past
  828.      the conventional memory limit, first column */
  829.   /* Begin stepping through memory, forcing uSegment to start on
  830.      a true "visual memory map" line number */
  831.   uSegment = 0xA000;
  832.   /* Make iMapSegment start on the appropriate line */
  833.   iMapSegment = uSegment / 1024;
  834.   iMapOffset  = 0;
  835.   iValidRom   = iFalse;
  836.   for (; uSegment <= END_SEGMENT; uSegment += SEGMENT_INC)
  837.     {
  838.       for (uOffset = START_OFFSET; uOffset < ENDING_OFFSET;
  839.            uOffset += OFFSET_INC)
  840.         {
  841.           /* Check the value of iValidRom. If it is non-zero, we are in  */
  842.           /*   an Option ROM and iValidRom is the number of 1K blocks    */
  843.           /*   that we still have to mark as being ROM. Mark the current */
  844.           /*   1K block as being ROM and then decrement iValidRom by 1.  */
  845.           /*   If iValidRom is zero, we are not currently inside an      */
  846.           /*   Option ROM area.                                          */
  847.           if (iValidRom)
  848.             {
  849.               iValidRom--;
  850.             }
  851.           else /* Not currently in an Option ROM */
  852.             {
  853.               /* Attempt to determine what is at address uSegment:uOffset.
  854.                  First check for an Option ROM with a valid signature */
  855.               iValidRom = OptionRomCheck (uSegment, uOffset);
  856.               if (iValidRom)
  857.                 {
  858.                   /* An Option ROM with a signature was detected with a
  859.                      length of iValidRom */
  860.                   if (wOptIndex < 10)
  861.                     {
  862.                       pRomMap->wOptRomLoc[wOptIndex]   = uSegment +
  863.                                                          (uOffset >> 4);
  864.                       pRomMap->dwOptRomSize[wOptIndex] = (DWORD) iValidRom *
  865.                                                          1024L;
  866.                       ++wOptIndex;
  867.                     }
  868.                   iValidRom--;
  869.                 }
  870.             }
  871.           /* Set the index for the memory map array to point to the
  872.              next column, same row */
  873.           iMapOffset++;
  874.         }
  875.       /* Set the indexes for the memory map array to point to the
  876.          next row, first column of the array */
  877.       iMapSegment++;
  878.       iMapOffset = 0;
  879.     }
  880.   return;
  881. }
  882. /************************************************************************
  883. *
  884. * Function Get_VisualMemMap()
  885. *
  886. * This function generates a visual memory map of the address range
  887. * 0000 to EFFF. It attempts to detect RAM, ROM and WOM (nothing).
  888. *
  889. * Mem_Info_St Fields Set
  890. * ----------------------
  891. *
  892. * auchMemoryMap = The array holding the characters used to display the
  893. *                 map.
  894. *
  895. *
  896. * Local Variables Used
  897. * --------------------
  898. *
  899. * iMapSegment, iMapOffset : Used to keep track of the location in the
  900. *                         : array holding the memory map.
  901. * iRow, iCol              : Used to point to a specific location in the
  902. *                         : array holding the memory map.
  903. * iMemLimit               : RAM and WOM extends up to the limit set
  904. *                         : by this variable
  905. * uSegment, uOffset       : The current segment:offset being looked at.
  906. * uiPageFrameAddress      : The address of EMS page frame.
  907. * Ram, Rom, Wom
  908. * NotCertain              : Used when building the memory map.
  909. * iFalse, iTrue           : Used for Boolean checking.
  910. * iValidRom               : Used to figure out if we are currently in a
  911. *                         : ROM area which had a valid signature byte.
  912. * END_SEGMENT             : The final segment to check through.
  913. *
  914. ************************************************************************/
  915. int Get_VisualMemMap (MEMORY_STRUCT FAR * pMem_Info, WORD wBusType)
  916. {
  917.   int  iMapSegment, iMapOffset;
  918.   int  iRow, iCol;
  919.   int  iMemLimit;
  920.   unsigned uSegment;
  921.   unsigned uOffset;
  922.   unsigned char Ram, Rom, Wom, NotCertain;
  923.   int iValidRom;
  924.   int iFalse = 0;
  925.   int iTrue = 1;
  926.   unsigned END_SEGMENT = 0xEC00;
  927.   /* Check if we are on a ps/2 or not. If this is a ps/2, the
  928.      range E000-EFFF is system ROM so we only need to check up
  929.      through the DC00 page.  If this is not a ps/2, the range
  930.      F000-EFFF is ROM so we only need to check up through page EC00 */
  931.   if (wBusType == 3)         /* Is this a PS/2? (MCA) */
  932.     END_SEGMENT = 0xDC00;
  933.   /* Define the characters to be used to display the map. Check
  934.      fpOutfile to determine if this map is going to the screen
  935.      or to a printer */
  936.   /* Going to the screen, use extended chars */
  937.   if (fReportFlag == FALSE)
  938.     {
  939.       Ram = DISPLAY_RAM;
  940.       Rom = DISPLAY_ROM;
  941.       Wom = DISPLAY_WOM;
  942.       NotCertain = DISPLAY_NOT_CERTAIN;
  943.     }
  944.   else /* Going to a report. Use standard ASCII */
  945.     {
  946.       Ram = REPORT_RAM;
  947.       Rom = REPORT_ROM;
  948.       Wom = REPORT_WOM;
  949.       NotCertain = REPORT_NOT_CERTAIN;
  950.     }
  951.   /* Special case for black and white screen display */
  952.   if (fBlackWhite == TRUE && fReportFlag == FALSE)
  953.     {
  954.       Rom = REPORT_ROM;
  955.     }
  956.   /* Initialize the map to RAM values in conventional RAM area  */
  957.   /* to RAM, the area up to the ROM BIOS as empty, and the ROM  */
  958.   /* BIOS as ROM.  Also, tack an ending null char in the last   */
  959.   /* column of each row so that winWrtStrnAttrib can be used    */
  960.   /* when moving the segment selection bar in the memory browse */
  961.   /* stuff.                                                     */
  962.   /* Fill up the "conventional RAM" in the visual memory map. */
  963.   /*   RAM is assumed to be installed in 16k incriments.      */
  964.   /* Set the maximum line number (ie, limit) to fill with RAM */
  965.   iMemLimit = (int) (pMem_Info->lConv_Mem / (16384L));
  966.   for (iRow = 0; iRow < iMemLimit; iRow++)
  967.     {
  968.       for (iCol = 0; iCol < (NUM_OF_COLS-1); iCol++)
  969.         pMem_Info->abMemoryMap [iRow][iCol] = Ram;
  970.       pMem_Info->abMemoryMap [iRow][iCol] = 0;
  971.     }
  972.   /* Fill from past "conventional RAM" to the ROM BIOS with WOM */
  973.   iMemLimit = (int) (END_SEGMENT / 1024);
  974.   for (; iRow <= iMemLimit; ++iRow)
  975.     {
  976.       for (iCol = 0; iCol < (NUM_OF_COLS-1); iCol++)
  977.         pMem_Info->abMemoryMap [iRow][iCol] = Wom;
  978.       pMem_Info->abMemoryMap [iRow][iCol] = 0;
  979.     }
  980.   /* Fill the ROM BIOS area with ROM */
  981.   for (; iRow < NUM_OF_ROWS; ++iRow)
  982.     {
  983.       for (iCol = 0; iCol < (NUM_OF_COLS-1); iCol++)
  984.         pMem_Info->abMemoryMap [iRow][iCol] = Rom;
  985.       pMem_Info->abMemoryMap [iRow][iCol] = 0;
  986.     }
  987.   /* Set the indexes for the memory map array to point to just past
  988.      the conventional memory limit, first column */
  989.   /* Begin stepping through memory, forcing uSegment to start on
  990.      a true "visual memory map" line number */
  991.   uSegment = (unsigned) (pMem_Info->lConv_Mem / 16L);
  992.   uSegment = uSegment & 0xF800;
  993.   /* Make iMapSegment start on the appropriate line */
  994.   iMapSegment = uSegment / 1024;
  995.   iMapOffset  = 0;
  996.   iValidRom   = iFalse;
  997.   for (; uSegment <= END_SEGMENT; uSegment += SEGMENT_INC)
  998.     {
  999.       for (uOffset = START_OFFSET; uOffset < ENDING_OFFSET;
  1000.            uOffset += OFFSET_INC)
  1001.         {
  1002.           /* Check the value of iValidRom. If it is non-zero, we are in  */
  1003.           /*   an Option ROM and iValidRom is the number of 1K blocks    */
  1004.           /*   that we still have to mark as being ROM. Mark the current */
  1005.           /*   1K block as being ROM and then decrement iValidRom by 1.  */
  1006.           /*   If iValidRom is zero, we are not currently inside an      */
  1007.           /*   Option ROM area.                                          */
  1008.           if (iValidRom)
  1009.             {
  1010.               pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = Rom;
  1011.               iValidRom--;
  1012.             }
  1013.           else /* Not currently in an Option ROM */
  1014.             {
  1015.               /* Attempt to determine what is at address uSegment:uOffset.
  1016.                  First check for an Option ROM with a valid signature */
  1017.               iValidRom = OptionRomCheck (uSegment, uOffset);
  1018.               if (iValidRom)
  1019.                 {
  1020.                   /* An Option ROM with a signature was detected with a
  1021.                      length of iValidRom */
  1022.                   pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = Rom;
  1023.                   iValidRom--;
  1024.                 }
  1025.               else /* Try to determine what is there */
  1026.                 {
  1027.                   switch (RamRomWomCheck (uSegment, uOffset, OFFSET_INC, fWindowsRunning))
  1028.                     {
  1029.                       case 0:
  1030.                         pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = Ram;
  1031.                         break;
  1032.                       case 1:
  1033.                         pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = NotCertain;
  1034.                         break;
  1035.                       case 2:
  1036.                         pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = Wom;
  1037.                         break;
  1038.                       default:
  1039.                         pMem_Info->abMemoryMap [iMapSegment][iMapOffset] = NotCertain;
  1040.                     }
  1041.                 }
  1042.             }
  1043.           /* Set the index for the memory map array to point to the
  1044.              next column, same row */
  1045.           iMapOffset++;
  1046.         }
  1047.       /* Set the indexes for the memory map array to point to the
  1048.          next row, first column of the array */
  1049.       iMapSegment++;
  1050.       iMapOffset = 0;
  1051.     }
  1052.   return (0);
  1053. }
  1054. /*********************************************************************
  1055.  * VisualMapOverlay - Fills the Visual Memory Map's Overlay map.  EMS
  1056.  *                    page frames are marked 'E', used UMBs are marked
  1057.  *                    'U', and free UMBs are marked 'F'.  Also counts
  1058.  *                    up the used and free UMBs.
  1059.  *
  1060.  * pMem - Pointer to the memory information structure.
  1061.  *
  1062.  * Returns:  TRUE if an error occured.
  1063.  *********************************************************************/
  1064. BOOL VisualMapOverlay (MEMORY_STRUCT FAR * pMem)
  1065. {
  1066.   WORD wPageFrameAddress;     /* Segment of EMS Page frame              */
  1067.   int iRow, iCol;             /* Row and Column of visual map overlay   */
  1068.   int iNumOfChars;            /* Number of characters (or 1K blocks)    */
  1069.                               /*   necessary to fill the page fram area */
  1070.   TSR_PROGRAMS_STRUCT *pTsr = NULL; /* TSR Program list                 */
  1071.   WORD wSize;                 /* Size required to store TSR list        */
  1072.   WORD i;                     /* Looping variable                       */
  1073.   /* Check to see if an EMS page frame was detected and if we want to */
  1074.   /*   display it.  If one was and we do want to show it, fill in the */
  1075.   /*   correct area in the memory map with characters to specify the  */
  1076.   /*   page frame                                                     */
  1077.   wPageFrameAddress = pMem->iPageFrameAddress;
  1078.   if (wPageFrameAddress)
  1079.     {
  1080.       iNumOfChars = 64;
  1081.       iRow = wPageFrameAddress / SEGMENT_INC;
  1082.       iCol = (wPageFrameAddress - (iRow * SEGMENT_INC)) / (OFFSET_INC >> 4);
  1083.       FillMemMapOverlay (pMem, iRow, iCol, iNumOfChars, DISPLAY_EMS);
  1084.     }
  1085.   /* Now, obtain the Memory Control Block information, to locate the  */
  1086.   /*   Upper Memory Blocks.                                           */
  1087.   if (wDosMajor >= 5 && wDosMajor < 10)
  1088.     {
  1089.       /* Obtain the TSR list */
  1090.       wSize = GetInfoSize (IDI_TSR_PROGRAMS_RECORD, FALSE);
  1091.       if ((pTsr = malloc (wSize)) == NULL)
  1092.         {
  1093.           OutOfMemory();
  1094.           return (TRUE);
  1095.         }
  1096.       /* Zero out the structure */
  1097.       memset (pTsr, '', wSize);
  1098.       if (GetInfo (IDI_TSR_PROGRAMS_RECORD, pTsr, FALSE, FALSE, FALSE))
  1099.         return (TRUE);
  1100.       /* Find the first upper memory block, if it exists */
  1101.       for (i = 1;
  1102.            pTsr[i].wAddress != 0 &&
  1103.            pTsr[i].wAddress + (pTsr[i].dwBlockSize >> 4) < 0xA000;
  1104.            ++i)
  1105.         ;
  1106.       /* Set the flag for UMBs available */
  1107.       if (pTsr[i].wAddress + (pTsr[i].dwBlockSize >> 4) >= 0xA000)
  1108.         pMem->fUmbsAvailable = TRUE;
  1109.       /* Begin walking the UMBs */
  1110.       for (; pTsr[i].wAddress != 0; ++i)
  1111.         {
  1112.           CHAR chFillChar;  /* Character to fill the visual map with */
  1113.           /* Skip the excluded UMB areas */
  1114.           if (strcmp (pTsr[i].szTsrName, pszExcludedUmbArea) == 0)
  1115.             continue;
  1116.           /* Free areas use 'F', used areas use 'U' */
  1117.           if (strcmp (pTsr[i].szTsrName, pszFreeMemory) == 0)
  1118.             {
  1119.               chFillChar = DISPLAY_FREE_UMB;
  1120.               /* Add up the UMB area, if it counts */
  1121.               if (pTsr[i].szTsrName[0] != ' ')
  1122.                 {
  1123.                   pMem->dwTotalUmbs += pTsr[i].dwBlockSize;
  1124.                   pMem->dwFreeUmbs  += pTsr[i].dwBlockSize;
  1125.                   if (pTsr[i].dwBlockSize > pMem->dwLargestFreeUmb)
  1126.                     pMem->dwLargestFreeUmb = pTsr[i].dwBlockSize;
  1127.                 }
  1128.             }
  1129.           else
  1130.             {
  1131.               chFillChar = DISPLAY_USED_UMB;
  1132.               /* Add up the UMB area, if it counts */
  1133.               if (pTsr[i].szTsrName[0] != ' ')
  1134.                 pMem->dwTotalUmbs += pTsr[i].dwBlockSize;
  1135.             }
  1136.           /* Fill the region with the correct value */
  1137.           iRow        = pTsr[i].wAddress / 0x400;
  1138.           iCol        = (pTsr[i].wAddress % 0x400) / 0x40;
  1139.           iNumOfChars = (WORD) ((pTsr[i].dwBlockSize + 1023) / 1024);
  1140.           FillMemMapOverlay (pMem, iRow, iCol, iNumOfChars, chFillChar);
  1141.         }
  1142.       /* Free up the TSR structure */
  1143.       free (pTsr);
  1144.     }
  1145. }
  1146. /************************************************************************
  1147. *
  1148. * Function FillMemMap()
  1149. *
  1150. * This function fills in a specified portion of the memory map array with
  1151. * a specific value.
  1152. *
  1153. *
  1154. * Mem_Info_St Fields Set
  1155. * ----------------------
  1156. *
  1157. * abMemoryMap = The array of unsigned characters which holds the values
  1158. *               used to display the memory map.
  1159. *
  1160. *
  1161. * Local Variables Used
  1162. * --------------------
  1163. *
  1164. * index       : An integer used to keep track of how many fill chars
  1165. *               have been written to the memory map array.
  1166. * iRow, iCol  : Integers used to index into the memory map array.
  1167. * iNumOfChars : The number of fill characters to write to the map array.
  1168. * uchFillChar : The fill character to be written to the map array.
  1169. *
  1170. ************************************************************************/
  1171. int FillMemMap (MEMORY_STRUCT FAR * pMem, int iRow, int iCol,
  1172.                    int iNumOfChars, unsigned char uchFillChar)
  1173. {
  1174.   int index = 0;
  1175.   while (index < iNumOfChars)
  1176.   {
  1177.     pMem->abMemoryMap [iRow][iCol] = uchFillChar;
  1178.     iCol++;
  1179.     if (iCol == NUM_OF_COLS - 1) /* Use (NUM_OF_COLS - 1) so we don't   */
  1180.     {                            /*   step on the null char used when   */
  1181.       iCol = 0;                  /*   displaying the map on the screen. */
  1182.       iRow++;
  1183.     }
  1184.     index++;
  1185.   }
  1186.   return (0);
  1187. }
  1188. /************************************************************************
  1189. *
  1190. * Function FillMemMapOverlay()
  1191. *
  1192. * This function fills in a specified portion of the memory map overlay
  1193. * array witha specific value.
  1194. *
  1195. *
  1196. * Mem_Info_St Fields Set
  1197. * ----------------------
  1198. *
  1199. * abMemMapOverlay = The array of unsigned characters which holds the
  1200. *                   values used to display the memory map.
  1201. *
  1202. *
  1203. * Local Variables Used
  1204. * --------------------
  1205. *
  1206. * index       : An integer used to keep track of how many fill chars
  1207. *               have been written to the memory map array.
  1208. * iRow, iCol  : Integers used to index into the memory map array.
  1209. * iNumOfChars : The number of fill characters to write to the map array.
  1210. * uchFillChar : The fill character to be written to the map array.
  1211. *
  1212. ************************************************************************/
  1213. int FillMemMapOverlay (MEMORY_STRUCT FAR * pMem, int iRow, int iCol,
  1214.                        int iNumOfChars, unsigned char uchFillChar)
  1215. {
  1216.   int index = 0;
  1217.   while (index < iNumOfChars)
  1218.   {
  1219.     pMem->abMemMapOverlay [iRow][iCol] = uchFillChar;
  1220.     iCol++;
  1221.     if (iCol == NUM_OF_COLS - 1) /* Use (NUM_OF_COLS - 1) so we don't   */
  1222.     {                            /*   step on the null char used when   */
  1223.       iCol = 0;                  /*   displaying the map on the screen. */
  1224.       iRow++;
  1225.     }
  1226.     index++;
  1227.   }
  1228.   return (0);
  1229. }
  1230. /**************************************************************************
  1231.  * RamRomWomCheck - Checks if the memory pointed to by uSegment:uOffset
  1232.  *                  is RAM, ROM, or Available (WOM).
  1233.  *
  1234.  *                  RAM can change.
  1235.  *                  WOM is unchangable and has FFH's through the entire
  1236.  *                      range.
  1237.  *                  ROM is unchangable and is not FFH through the entire
  1238.  *                      range.
  1239.  *
  1240.  * uSegment - Segment to search
  1241.  *
  1242.  * uOffset  - Offset to search
  1243.  *
  1244.  * uLength  - Length of search to perform when checking for ROM/WOM
  1245.  *
  1246.  * fWindowsRunning - If windows is running, disable the RAM test.
  1247.  *
  1248.  * Returns RAM (0), ROM (1), or WOM (2).
  1249.  **************************************************************************/
  1250. int _fastcall RamRomWomCheck (unsigned uSegment,
  1251.                               unsigned uOffset,
  1252.                               unsigned uLength,
  1253.                               BOOL     fWindowsRunning)
  1254. {
  1255.   UCHAR uchHoldByte;            /* Used for storing byte during RAM check */
  1256.   int   iReturnValue;           /* Value this routine returns */
  1257.   _asm
  1258.     {
  1259.       push  es                  ;Save the ES register
  1260.       push  ax                  ;Put uSegment into ES
  1261.       pop   es
  1262.       mov   di,dx               ;Put uOffset into DI
  1263.       mov   cx,bx               ;Put uLength into CX
  1264.       mov   al,es:[di]          ;Store byte in holding area
  1265.       mov   [uchHoldByte],al
  1266.       cmp   fWindowsRunning, 0  ;Is it safe to perform the RAM test?
  1267.       jne   SkipRamTest         ;Skip out if not.
  1268.       xor   al,0xFF             ;Toggle all bits in AL
  1269.       cli                       ;Clear interrupts for RAM check
  1270.       mov   es:[di],al          ;Put new value back in memory
  1271.       mov   al,[uchHoldByte]    ;Put the original value back in AL
  1272.                                 ;  There is logic behind checking the
  1273.                                 ;  original value instead of the changed
  1274.                                 ;  value.  Suppose the adapter card at
  1275.                                 ;  this address does not support all 8
  1276.                                 ;  bits.  Checking for the changed value
  1277.                                 ;  would return false, even though seven
  1278.                                 ;  bits were changed.  (One possibility
  1279.                                 ;  for the missing bit or bits would be
  1280.                                 ;  special purpose RAM).
  1281.       push  cx                  ;Wait for a moment, to allow memory to
  1282.       mov   cx,0x10             ;  stabilize
  1283.     Wait001:
  1284.       loop  Wait001
  1285.       pop   cx
  1286.       cmp   al,es:[di]          ;Check to see if it is the original value
  1287.       jne   ThisMayBeRAM        ;If not, this might be RAM
  1288.     ;---- Now check for the difference between ROM and WOM ----
  1289.     SkipRamTest:
  1290.       cmp   al,0xFF             ;Was the byte 0FFH?
  1291.       jne   ThisIsROM           ;If not, this is definitely ROM
  1292.     ;---- This could be WOM.  Check for a bunch of FFHs ----
  1293.       repe  scasb               ;Search for the first non 0FFH
  1294.       cmp   cx,0                ;Did we get to the end without a non-0FFH?
  1295.       jne   ThisIsROM           ;If not, this is ROM
  1296.     ;---- WOM was detected, set the return value ----
  1297.       mov   iReturnValue,WOM    ;Set the return value
  1298.       jmp   MemCheckComplete    ;Exit this routine
  1299.     ;---- RAM may have been detected ----
  1300.     ThisMayBeRAM:
  1301.       mov   bl,es:[di]          ;Now check to see if it is the changed value.
  1302.       xor   bl,0xFF             ;  The value may be bus noise or some other
  1303.                                 ;  unusual case (WYSE 286 w/ 640k).  If it
  1304.                                 ;  doesn't match the changed value, it is
  1305.                                 ;  not RAM.
  1306.       cmp   al,bl               ;Does it match the changed value
  1307.       jne   ThisIsNotRAM        ;If not, this is something else
  1308.     ;---- RAM really was detected ----
  1309.       mov   al,uchHoldByte      ;Restore the original byte
  1310.       mov   es:[di],al
  1311.       mov   iReturnValue,RAM    ;Set the return value
  1312.       jmp   MemCheckComplete    ;Exit this routine
  1313.     ;---- ROM was detected, set the return value ----
  1314.     ThisIsROM:
  1315.       mov   iReturnValue,ROM    ;Set the return value
  1316.       jmp   MemCheckComplete    ;Exit this routine
  1317.     ;---- It's not RAM, it's not ROM, it's not WOM, it's something else ----
  1318.     ThisIsNotRAM:
  1319.       mov   iReturnValue,ROM    ;We'll call it available for now
  1320.       jmp   MemCheckComplete    ;Exit this routine
  1321.     ;---- Test is completed, return to calling routine ----
  1322.     MemCheckComplete:
  1323.       sti                       ;Restore interrupts
  1324.       pop   es                  ;Restore the original ES register
  1325.     }
  1326.   return (iReturnValue);
  1327. }
  1328. /**************************************************************************
  1329.  * OptionRomCheck - Checks for the presence of installed Option ROM cards
  1330.  *                  (ie, EGA/VGA, Net cards with ROMs, etc).
  1331.  *
  1332.  *                  Option ROM cards are installed in the range of C000
  1333.  *                    (A000?) - EFFF for non-PS/2 computers.  PS/2s use
  1334.  *                    E000-FFFF, so option cards can only be installed
  1335.  *                    between C000 - DFFF.
  1336.  *
  1337.  *                  According to the limited documentation we've found,
  1338.  *                    (Specifically a Windows 3.00 assembly language
  1339.  *                    include file), all Option ROMs in the valid range
  1340.  *                    start with the bytes AAH, 55H, followed by a byte
  1341.  *                    that tells the length of the ROM card / 512:
  1342.  *
  1343.  *                    +----------------------+
  1344.  *                    |         AAH          |
  1345.  *                    +----------------------+
  1346.  *                    |         55H          |
  1347.  *                    +----------------------+
  1348.  *                    | Byte: ROM Size / 512 |
  1349.  *                    +----------------------+
  1350.  *                    |/ / / / / / / / / / / |
  1351.  *                    | /-Option ROM code-/  |
  1352.  *                    |/ / / / / / / / / / / |
  1353.  *                    +----------------------+
  1354.  *                    (Sum of all bytes MOD 100H is 00H).
  1355.  *
  1356.  *                  I add up all the bytes starting from th AAH into BL,
  1357.  *                    allowing BL to overflow (an 8 bit checksum sort
  1358.  *                    of arrangement).  If the value is 00H, this was
  1359.  *                    a valid option card.  If not, I stumbled across
  1360.  *                    a spurious 55 AA in memory.
  1361.  *
  1362.  * uSegment - Segment to search.
  1363.  *
  1364.  * uOffset  - Offset to search.
  1365.  *
  1366.  * Returns: Number of 1K pages for the Option ROM starting at the
  1367.  *          specified location, or 0 if no Option ROM was found.
  1368.  **************************************************************************/
  1369. int _fastcall OptionRomCheck (unsigned uSegment, unsigned uOffset)
  1370. {
  1371.   int   iReturnValue;           /* Value this routine returns */
  1372.   _asm
  1373.     {
  1374.                                 ;uSegment is in AX
  1375.                                 ;uOffset  is in DX
  1376.       push  es                  ;Save the ES register
  1377.     ;---- Adjust Segment so Offset can be :0000 ----
  1378.       mov   cl,4                ;Shift uOffset 4 bits
  1379.       shr   dx,cl
  1380.       add   ax,dx               ;Add to uSegment
  1381.       push  ax                  ;Put uSegment into ES
  1382.       pop   es
  1383.       xor   di,di               ;Put zero into DI
  1384.     ;---- Check for ROM signature ----
  1385.       cmp   es:[di],0xAA55      ;Is the ROM signature present?
  1386.       jne   NotOptionRom        ;If not, jump out.
  1387.     ;---- ROM signature detected, Set return value in case it's valid ----
  1388.       xor   cx,cx               ;Zero out CX register
  1389.       mov   cl,es:[di + 2]      ;Load the number of bytes / 512
  1390.       test  cl,0x01             ;Is it an odd value?
  1391.       jz    SetReturnValue      ;If not, set return value
  1392.       inc   cx                  ;If odd, increase CX to even 1K boundry
  1393.     SetReturnValue:
  1394.       shr   cx,1                ;Divide by 2 to obtain Option ROM size in K
  1395.       mov   iReturnValue,cx     ;Set the return value, in case this is
  1396.                                 ;  a value Option ROM.
  1397.     ;---- Prepare for checksum on Option ROM ----
  1398.       xor   cx,cx               ;Zero out CX register
  1399. ;     inc   di                  ;Bump DI three bytes to move around the
  1400. ;     inc   di                  ;  ROM signature
  1401. ;                               ;Load the number of bytes in the Option ROM
  1402. ;                               ;  into CX
  1403.       mov   ch,es:[di + 2]      ;CL = byte count / 512 (CX = byte count / 2)
  1404.       shl   cx,1                ;CX now = number of bytes
  1405.       xor   al,al               ;Zero out the checksum register
  1406.     ;---- Perform checksum ----
  1407.     CkSumLoop:
  1408.       add   al,es:[di]          ;Add byte to AL (overflow is expected)
  1409.       inc   di                  ;Bump DI to point to next byte
  1410.       loop  CkSumLoop           ;Loop until all bytes have been read
  1411.     ;---- Checksum is complete, AL = 0 means this is a valid Option ROM ----
  1412.       cmp   al,0                ;Is the checksum zero?
  1413.       je    OptionCheckComplete ;If so, the return value has already been
  1414.                                 ;  set, so we jump out of the routine
  1415.                                 ;If not, fall through...
  1416.     ;---- ROM Signature not Detected ----
  1417.     NotOptionRom:
  1418.       mov   iReturnValue,0      ;Set the return value to zero.
  1419.       jmp   OptionCheckComplete ;Exit this routine
  1420.     ;---- Test is completed, return to calling routine ----
  1421.     OptionCheckComplete:
  1422.       pop   es                  ;Restore the original ES register
  1423.     }
  1424.   return (iReturnValue);
  1425. }
  1426. /*********************************************************************
  1427.  * SprintMemInfo - Put Memory information into a set of strings to be
  1428.  *                 printed or displayed.
  1429.  *
  1430.  * fMemoryType  - Type(s) of memory to put into strings (MEM_ALL to
  1431.  *                show all types of memory).
  1432.  * pMem         - Pointer to the structure describing the memory
  1433.  *                types.
  1434.  * szSumStrings - Summary information string holder.
  1435.  *
  1436.  * Returns:  NULL if an error occured, or if summary information is
  1437.  *           requested.
  1438.  *********************************************************************/
  1439. QSZ * SprintMemInfo (BOOL fMemoryType,
  1440.                      MEMORY_STRUCT *pMem,
  1441.                      CHAR szSumStrings[][MAX_SUMM_INFO + 5],
  1442.                      BOOL fOverlayFlag)
  1443. {
  1444.   WORD wNmbrStrings;        /* Number of strings                     */
  1445.   WORD wNmbrChars;          /* Number of characters in the strings   */
  1446.   WORD i;                   /* Index variable                        */
  1447.   CHAR chBuffer[120];       /* Local string                          */
  1448.   QSZ  *pqszStrings;        /* Location for storing string pointers  */
  1449.   WORD wAlignColumn;        /* Column to align titles                */
  1450.   /* Summary strings */
  1451.   if (szSumStrings != NULL)
  1452.     {
  1453.       i = 0;
  1454.       /* Conventional */
  1455.       sprintf (chBuffer, "%ldK", pMem->lConv_Mem / 1024L);
  1456.       strncpy (szSumStrings[i], chBuffer, MAX_SUMM_INFO + 3);
  1457.       /* Extended */
  1458.       if (pMem->iCMOSExtended)
  1459.         {
  1460.           strncat (szSumStrings[i], pszCommaSpace,
  1461.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1462.   sprintf (chBuffer, "%uK ", (unsigned int)pMem->iCMOSExtended);
  1463.           strcat (chBuffer, "Ext");
  1464.           if (strlen (chBuffer) + strlen (szSumStrings[i]) > MAX_SUMM_INFO + 3)
  1465.             {
  1466.               i = 1;
  1467.               szSumStrings[i][0] = '';
  1468.             }
  1469.           strncat (szSumStrings[i], chBuffer,
  1470.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1471.         }
  1472.       /* EMS */
  1473.       if (pMem->iTotal_Emm_Pages)
  1474.         {
  1475.           strncat (szSumStrings[i], pszCommaSpace,
  1476.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1477.   sprintf (chBuffer, "%uK ", (unsigned int)pMem->iTotal_Emm_Pages * 16);
  1478.           strcat (chBuffer, "EMS");
  1479.           if (strlen (chBuffer) + strlen (szSumStrings[i]) > MAX_SUMM_INFO + 3)
  1480.             {
  1481.               i = 1;
  1482.               szSumStrings[i][0] = '';
  1483.             }
  1484.           strncat (szSumStrings[i], chBuffer,
  1485.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1486.         }
  1487.       /* XMS */
  1488.       if (pMem->iXmm_Free_Err == 0 && pMem->iTotal_Free_Xm != 0)
  1489.         {
  1490.           strncat (szSumStrings[i], pszCommaSpace,
  1491.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1492.   sprintf (chBuffer, "%uK ", (unsigned int)pMem->iTotal_Free_Xm);
  1493.           strcat (chBuffer, "XMS");
  1494.           if (strlen (chBuffer) + strlen (szSumStrings[i]) > MAX_SUMM_INFO + 3)
  1495.             {
  1496.               i = 1;
  1497.               szSumStrings[i][0] = '';
  1498.             }
  1499.           strncat (szSumStrings[i], chBuffer,
  1500.                    MAX_SUMM_INFO + 3 - strlen (szSumStrings[i]));
  1501.         }
  1502.       return (NULL);
  1503.     }
  1504.   /* Overestimate the amount of space required for the strings */
  1505.   switch (fMemoryType)
  1506.     {
  1507.       case MEM_ALL:
  1508.         wAlignColumn = MEM_MAX_ALIGN;
  1509.         wNmbrStrings = MEM_CONV_STRINGS + MEM_EXT_STRINGS  +
  1510.                        MEM_EMS_STRINGS  + MEM_XMS_STRINGS  +
  1511.                        MEM_VCPI_STRINGS + MEM_DPMI_STRINGS +
  1512.                        MEM_LEGEND_STRINGS;
  1513.         if (wNmbrStrings < MEM_640K_LIMIT)
  1514.           wNmbrStrings = MEM_640K_LIMIT;
  1515.         wNmbrChars   = 2450;  /* Beyond the maximum chars required */
  1516.         break;
  1517.       case MEM_SUMMARY:
  1518.         wAlignColumn =  0;
  1519.         wNmbrStrings =  1;
  1520.         wNmbrChars   = REPORT_WIDTH + 1;
  1521.         break;
  1522.       case MEM_CONVENTIONAL:
  1523.         wAlignColumn = MEM_CONV_ALIGN;
  1524.         wNmbrStrings = MEM_CONV_STRINGS;
  1525.         wNmbrChars   = MEM_CONV_STRINGS * REPORT_WIDTH + 1;
  1526.         break;
  1527.       case MEM_EXTENDED:
  1528.         wAlignColumn = MEM_EXT_ALIGN;
  1529.         wNmbrStrings = MEM_EXT_STRINGS;
  1530.         wNmbrChars   = MEM_EXT_STRINGS * REPORT_WIDTH + 1;
  1531.         break;
  1532.       case MEM_EMS:
  1533.         wAlignColumn = MEM_EMS_ALIGN;
  1534.         wNmbrStrings = MEM_EMS_STRINGS;
  1535.         wNmbrChars   = MEM_EMS_STRINGS * REPORT_WIDTH + 1;
  1536.         break;
  1537.       case MEM_XMS:
  1538.         wAlignColumn = MEM_XMS_ALIGN;
  1539.         wNmbrStrings = MEM_XMS_STRINGS;
  1540.         wNmbrChars   = MEM_XMS_STRINGS * REPORT_WIDTH + 1;
  1541.         break;
  1542.       case MEM_VCPI:
  1543.         wAlignColumn = MEM_VCPI_ALIGN;
  1544.         wNmbrStrings = MEM_VCPI_STRINGS;
  1545.         wNmbrChars   = MEM_VCPI_STRINGS * REPORT_WIDTH + 1;
  1546.         break;
  1547.       case MEM_DPMI:
  1548.         wAlignColumn = MEM_DPMI_ALIGN;
  1549.         wNmbrStrings = MEM_DPMI_STRINGS;
  1550.         wNmbrChars   = MEM_DPMI_STRINGS * REPORT_WIDTH + 1;
  1551.         break;
  1552.       case MEM_VISUAL_MAP:
  1553.         wAlignColumn = 12;
  1554.         wNmbrStrings = 65;
  1555.         wNmbrChars   = wNmbrStrings * 40;
  1556.         break;
  1557.       default:
  1558.         wAlignColumn =  0;
  1559.         wNmbrStrings =  1;
  1560.         wNmbrChars   = REPORT_WIDTH + 1;
  1561.     }
  1562.   /* Allocate space for the pointer area and string area */
  1563.   pqszStrings = AllocStringSpace (wNmbrStrings, wNmbrChars);
  1564.   if (pqszStrings == NULL)
  1565.     return (NULL);
  1566.   /* Put the information in place */
  1567.   i = 0;
  1568.   /* Summary Information */
  1569.   if (fMemoryType == MEM_SUMMARY)
  1570.     {
  1571.       return (NULL);
  1572.     }
  1573.   /* Include the memory legend */
  1574.   if (fReportFlag)
  1575.     {
  1576.       sprintf (chBuffer, "Legend:  Available "%c%c"  RAM "%c%c"  ROM "%c%c"  Possibly Available "%c%c"",
  1577.                REPORT_WOM, REPORT_WOM,
  1578.                REPORT_RAM, REPORT_RAM,
  1579.                REPORT_ROM, REPORT_ROM,
  1580.                REPORT_NOT_CERTAIN, REPORT_NOT_CERTAIN);
  1581.       Qstrcpy (pqszStrings[i], chBuffer);
  1582.       PrepNextString (pqszStrings, i++);
  1583.       sprintf (chBuffer, "  EMS Page Frame "%c%c"",
  1584.                REPORT_EMS, REPORT_EMS);
  1585.       Qstrcpy (pqszStrings[i], chBuffer);
  1586.       if (wDosMajor >= 5 && wDosMajor < 10)
  1587.         {
  1588.           sprintf (chBuffer, "  Used UMBs "%c%c"  Free UMBs "%c%c"",
  1589.                    REPORT_USED_UMB, REPORT_USED_UMB,
  1590.                    REPORT_FREE_UMB, REPORT_FREE_UMB);
  1591.           Qstrcat (pqszStrings[i], chBuffer);
  1592.         }
  1593.       if (pMem->fXmsUmbAvailable)
  1594.         {
  1595.           sprintf (chBuffer, "  Free XMS UMBs "%c%c"",
  1596.                    REPORT_FREE_XMS_UMB, REPORT_FREE_XMS_UMB);
  1597.           Qstrcat (pqszStrings[i], chBuffer);
  1598.         }
  1599.       PrepNextString (pqszStrings, i++);
  1600.     }
  1601.   else
  1602.     {
  1603.       sprintf (chBuffer, "Legend:  Available "&1%c%c&0"  RAM "&1%c%c&0"  ROM "&1%c%c&0"  Possibly Available "&1%c%c&0"",
  1604.                DISPLAY_WOM, DISPLAY_WOM,
  1605.                DISPLAY_RAM, DISPLAY_RAM,
  1606.                (fBlackWhite) ? REPORT_ROM : DISPLAY_ROM,
  1607.                (fBlackWhite) ? REPORT_ROM : DISPLAY_ROM,
  1608.                DISPLAY_NOT_CERTAIN, DISPLAY_NOT_CERTAIN);
  1609.       Qstrcpy (pqszStrings[i], chBuffer);
  1610.       PrepNextString (pqszStrings, i++);
  1611.       sprintf (chBuffer, "  EMS Page Frame "&1%c%c&0"",
  1612.                DISPLAY_EMS, DISPLAY_EMS);
  1613.       Qstrcpy (pqszStrings[i], chBuffer);
  1614.       if (wDosMajor >= 5 && wDosMajor < 10)
  1615.         {
  1616.           sprintf (chBuffer, "  Used UMBs "&1%c%c&0"  Free UMBs "&1%c%c&0"",
  1617.                    DISPLAY_USED_UMB, DISPLAY_USED_UMB,
  1618.                    DISPLAY_FREE_UMB, DISPLAY_FREE_UMB);
  1619.           Qstrcat (pqszStrings[i], chBuffer);
  1620.         }
  1621.       if (pMem->fXmsUmbAvailable)
  1622.         {
  1623.           sprintf (chBuffer, "  Free XMS UMBs "&1%c%c&0"",
  1624.                    DISPLAY_FREE_XMS_UMB, DISPLAY_FREE_XMS_UMB);
  1625.           Qstrcat (pqszStrings[i], chBuffer);
  1626.         }
  1627.       PrepNextString (pqszStrings, i++);
  1628.     }
  1629.   /* Conventional memory */
  1630.   if (fMemoryType == MEM_CONVENTIONAL || fMemoryType == MEM_ALL)
  1631.     {
  1632.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1633.       /* Conventional Titles */
  1634.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_CONV_TITLE]);
  1635.       PrepNextString (pqszStrings, i++);
  1636.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1637.       /* Total Conventional Memory */
  1638.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_CONV_TOTAL],
  1639.                    wAlignColumn);
  1640.       sprintf (chBuffer, "%ldK", pMem->lConv_Mem / 1024L);
  1641.       Qstrcat (pqszStrings[i], chBuffer);
  1642.       PrepNextString (pqszStrings, i++);
  1643.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1644.       /* Available Conventional Memory, Line 1 */
  1645.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_CONV_AVAIL],
  1646.                    wAlignColumn);
  1647.       sprintf (chBuffer, "%ldK", pMem->lFree_Conv_Mem / 1024L);
  1648.       Qstrcat (pqszStrings[i], chBuffer);
  1649.       PrepNextString (pqszStrings, i++);
  1650.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1651.       /* Available Conventional Memory, Line 2 */
  1652.       QstrcatAlign (pqszStrings[i], pszNull, wAlignColumn);
  1653.       sprintf (chBuffer, "%ld bytes", pMem->lFree_Conv_Mem);
  1654.       Qstrcat (pqszStrings[i], chBuffer);
  1655.       PrepNextString (pqszStrings, i++);
  1656.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1657.       /* Separator when sprinting all types of memory */
  1658.       if (fMemoryType == MEM_ALL)
  1659.         {
  1660.           PrepNextString (pqszStrings, i++);
  1661.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1662.         }
  1663.     }
  1664.   /* Extended memory */
  1665.   if (fMemoryType == MEM_EXTENDED || fMemoryType == MEM_ALL)
  1666.     {
  1667.       /* Extended Titles */
  1668.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_EXT_TITLE]);
  1669.       PrepNextString (pqszStrings, i++);
  1670.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1671.       /* Total Extended Memory */
  1672.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_CONV_TOTAL],
  1673.                    wAlignColumn);
  1674.       sprintf (chBuffer, "%uK", (unsigned int)pMem->iCMOSExtended);
  1675.       Qstrcat (pqszStrings[i], chBuffer);
  1676.       PrepNextString (pqszStrings, i++);
  1677.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1678.       /* Separator when sprinting all types of memory */
  1679.       if (fMemoryType == MEM_ALL)
  1680.         {
  1681.           PrepNextString (pqszStrings, i++);
  1682.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1683.         }
  1684.     }
  1685.   /* UMB Information */
  1686.   if (pMem->fUmbsAvailable &&
  1687.       (fMemoryType == MEM_CONVENTIONAL || fMemoryType == MEM_ALL))
  1688.     {
  1689.       /* UMB Titles */
  1690.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_UMB_TITLE]);
  1691.       PrepNextString (pqszStrings, i++);
  1692.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1693.       /* Total UMBs */
  1694.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_UMB_TOTAL],
  1695.                    wAlignColumn);
  1696.       sprintf (chBuffer, "%luK", pMem->dwTotalUmbs / 1024L);
  1697.       Qstrcat (pqszStrings[i], chBuffer);
  1698.       PrepNextString (pqszStrings, i++);
  1699.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1700.       /* Total Free UMBs */
  1701.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_UMB_TOTAL_FREE],
  1702.                    wAlignColumn);
  1703.       sprintf (chBuffer, "%luK", pMem->dwFreeUmbs / 1024L);
  1704.       Qstrcat (pqszStrings[i], chBuffer);
  1705.       PrepNextString (pqszStrings, i++);
  1706.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1707.       /* Largest Free UMBs */
  1708.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_UMB_LARGEST_FREE],
  1709.                    wAlignColumn);
  1710.       sprintf (chBuffer, "%luK", pMem->dwLargestFreeUmb / 1024L);
  1711.       Qstrcat (pqszStrings[i], chBuffer);
  1712.       PrepNextString (pqszStrings, i++);
  1713.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1714.       /* Separator when sprinting all types of memory */
  1715.       if (fMemoryType == MEM_ALL)
  1716.         {
  1717.           PrepNextString (pqszStrings, i++);
  1718.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1719.         }
  1720.     }
  1721.   /* EMS Information */
  1722.   if (fMemoryType  == MEM_EMS ||
  1723.       (fMemoryType == MEM_ALL && pMem->iEmm_Is_There))
  1724.     {
  1725.       /* EMS Titles */
  1726.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_EMS_TITLE]);
  1727.       PrepNextString (pqszStrings, i++);
  1728.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1729.       /* EMS Version */
  1730.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_EMS_VERSION],
  1731.                    wAlignColumn);
  1732.       sprintf (chBuffer, "%d.%02d",
  1733.                pMem->iEmm_VersionMajor,
  1734.                pMem->iEmm_VersionMinor);
  1735.       Qstrcat (pqszStrings[i], chBuffer);
  1736.       PrepNextString (pqszStrings, i++);
  1737.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1738.       /* EMS Page Frame */
  1739.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_EMS_PAGE_FRAME],
  1740.                    wAlignColumn);
  1741.       if (pMem->iPageFrameAddress)
  1742.         {
  1743.           sprintf (chBuffer, "%04XH", pMem->iPageFrameAddress);
  1744.           Qstrcat (pqszStrings[i], chBuffer);
  1745.         }
  1746.       else
  1747.         Qstrcat (pqszStrings[i], pszNoPageFrame);
  1748.       PrepNextString (pqszStrings, i++);
  1749.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1750.       /* Total EMS */
  1751.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_EMS_TOTAL],
  1752.                    wAlignColumn);
  1753.       sprintf (chBuffer, "%uK", (unsigned int)pMem->iTotal_Emm_Pages * 16);
  1754.       Qstrcat (pqszStrings[i], chBuffer);
  1755.       PrepNextString (pqszStrings, i++);
  1756.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1757.       /* Available EMS */
  1758.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_EMS_AVAIL],
  1759.                    wAlignColumn);
  1760.       sprintf (chBuffer, "%dK", pMem->iFree_Emm_Pages * 16);
  1761.       Qstrcat (pqszStrings[i], chBuffer);
  1762.       PrepNextString (pqszStrings, i++);
  1763.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1764.       /* Separator when sprinting all types of memory */
  1765.       if (fMemoryType == MEM_ALL)
  1766.         {
  1767.           PrepNextString (pqszStrings, i++);
  1768.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1769.         }
  1770.     }
  1771.   /* XMS Information */
  1772.   if (fMemoryType  == MEM_XMS ||
  1773.       (fMemoryType == MEM_ALL && pMem->iXmm_Is_There))
  1774.     {
  1775.       /* XMS Titles */
  1776.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_XMS_TITLE]);
  1777.       PrepNextString (pqszStrings, i++);
  1778.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1779.       /* XMS Version */
  1780.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_VERSION],
  1781.                    wAlignColumn);
  1782.       sprintf (chBuffer, "%x.%02x",
  1783.                pMem->uchXmm_Spec_VersionMajor,
  1784.                pMem->uchXmm_Spec_VersionMinor);
  1785.       Qstrcat (pqszStrings[i], chBuffer);
  1786.       PrepNextString (pqszStrings, i++);
  1787.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1788.       /* XMS Driver Version */
  1789.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_DRIVER_VER],
  1790.                    wAlignColumn);
  1791.       sprintf (chBuffer, "%x.%02x",
  1792.                pMem->uchXmm_Driver_VersionMajor,
  1793.                pMem->uchXmm_Driver_VersionMinor);
  1794.       Qstrcat (pqszStrings[i], chBuffer);
  1795.       PrepNextString (pqszStrings, i++);
  1796.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1797.       /* A20 Line */
  1798.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_A20_LINE],
  1799.                    wAlignColumn);
  1800.       if (pMem->iA20Status)
  1801.         Qstrcat (pqszStrings[i], pszEnabled);
  1802.       else
  1803.         Qstrcat (pqszStrings[i], pszNotEnabled);
  1804.       PrepNextString (pqszStrings, i++);
  1805.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1806.       /* High Memory Area */
  1807.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_HMA],
  1808.                    wAlignColumn);
  1809.       if (pMem->iXmm_Is_There) /* There is an XMS driver present */
  1810.         {
  1811.           switch (pMem->iXMSError) /* Check the status of any error codes */
  1812.             {                             /* returned when querying the XMS driver */
  1813.               case 0:
  1814.               case 6: Qstrcat (pqszStrings[i], "Available");
  1815.                       break;              /* Case 6 also means there was a problem releasing the HMA */
  1816.               case 1: Qstrcat (pqszStrings[i], "Request failed");
  1817.                       break;
  1818.               case 2: Qstrcat (pqszStrings[i], "VDISK detected");
  1819.                       break;
  1820.               case 3: Qstrcat (pqszStrings[i], "Not present");
  1821.                       break;
  1822.               case 4: Qstrcat (pqszStrings[i], "In use");
  1823.                       break;
  1824.               default: ;
  1825.             }
  1826.         }
  1827.       else
  1828.         Qstrcat (pqszStrings[i], "Not available"); /* An XMS driver is not present */
  1829.       PrepNextString (pqszStrings, i++);
  1830.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1831.       /* Available XMS */
  1832.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_AVAIL],
  1833.                    wAlignColumn);
  1834.       if (pMem->iXmm_Free_Err)
  1835.         Qstrcat (pqszStrings[i], pszError);
  1836.       else
  1837.         {
  1838.   sprintf(chBuffer, "%uK", (unsigned int)pMem->iTotal_Free_Xm);
  1839.           Qstrcat (pqszStrings[i], chBuffer);
  1840.         }
  1841.       PrepNextString (pqszStrings, i++);
  1842.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1843.       /* Largest Available XMS */
  1844.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_LARGEST_AVAIL],
  1845.                    wAlignColumn);
  1846.       if (pMem->iXmm_Free_Err == 0)
  1847.         {
  1848.   sprintf(chBuffer, "%uK", (unsigned int)pMem->iLargest_Free_Xm);
  1849.           Qstrcat (pqszStrings[i], chBuffer);
  1850.           PrepNextString (pqszStrings, i++);
  1851.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1852.         }
  1853.       else
  1854.         Qstrcat (pqszStrings[i], pszError);
  1855.       /* Super Extended Memory */
  1856.       if (pMem->fSxmsAvailable)
  1857.         {
  1858.           /* Available SXMS */
  1859.           QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_SXMS_AVAIL],
  1860.                        wAlignColumn);
  1861.           sprintf(chBuffer, "%luK", pMem->dwSxmsTotalFree);
  1862.           Qstrcat (pqszStrings[i], chBuffer);
  1863.           PrepNextString (pqszStrings, i++);
  1864.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1865.           /* Largest Free SXMS */
  1866.           QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_SXMS_LARGEST_AVAIL],
  1867.                        wAlignColumn);
  1868.           sprintf(chBuffer, "%luK", pMem->dwSxmsLargestFree);
  1869.           Qstrcat (pqszStrings[i], chBuffer);
  1870.           PrepNextString (pqszStrings, i++);
  1871.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1872.         }
  1873.       if (pMem->fXmsUmbAvailable)
  1874.         {
  1875.           /* Largest Available XMS UMB */
  1876.           QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_TOTAL_UMB_AVAIL],
  1877.                        wAlignColumn);
  1878.           sprintf(chBuffer, "%luK", pMem->dwXmsUmbFree / 1024L);
  1879.           Qstrcat (pqszStrings[i], chBuffer);
  1880.           PrepNextString (pqszStrings, i++);
  1881.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1882.           /* Largest Available XMS UMB */
  1883.           QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_XMS_LARGEST_UMB_AVAIL],
  1884.                        wAlignColumn);
  1885.           sprintf(chBuffer, "%luK", pMem->dwXmsUmbLargestFree / 1024L);
  1886.           Qstrcat (pqszStrings[i], chBuffer);
  1887.           PrepNextString (pqszStrings, i++);
  1888.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1889.         }
  1890.       /* Separator when sprinting all types of memory */
  1891.       if (fMemoryType == MEM_ALL)
  1892.         {
  1893.           PrepNextString (pqszStrings, i++);
  1894.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1895.         }
  1896.     }
  1897.   /* VCPI Information */
  1898.   if (fMemoryType  == MEM_VCPI ||
  1899.       (fMemoryType == MEM_ALL && pMem->iVCPIPresent))
  1900.     {
  1901.       /* VCPI Titles */
  1902.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_VCPI_TITLE]);
  1903.       PrepNextString (pqszStrings, i++);
  1904.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1905.       /* VCPI Detected */
  1906.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_VCPI_DETECTED],
  1907.                    wAlignColumn);
  1908.       if (pMem->iVCPIPresent)
  1909.         Qstrcat (pqszStrings[i], pszYes);
  1910.       else
  1911.         Qstrcat (pqszStrings[i], pszNo);
  1912.       PrepNextString (pqszStrings, i++);
  1913.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1914.       /* VCPI Version */
  1915.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_VCPI_VERSION],
  1916.                    wAlignColumn);
  1917.       sprintf (chBuffer, "%d.%02d", pMem->iVCPIMajorVersion,
  1918.                pMem->iVCPIMinorVersion);
  1919.       Qstrcat (pqszStrings[i], chBuffer);
  1920.       PrepNextString (pqszStrings, i++);
  1921.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1922.       /* VCPI Available */
  1923.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_VCPI_AVAIL],
  1924.                    wAlignColumn);
  1925.       sprintf (chBuffer, "%dK", pMem->iVCPIAvailMemory);
  1926.       Qstrcat (pqszStrings[i], chBuffer);
  1927.       PrepNextString (pqszStrings, i++);
  1928.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1929.       /* Separator when sprinting all types of memory */
  1930.       if (fMemoryType == MEM_ALL)
  1931.         {
  1932.           PrepNextString (pqszStrings, i++);
  1933.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1934.         }
  1935.     }
  1936.   /* DPMI Information */
  1937.   if (fMemoryType  == MEM_DPMI ||
  1938.       (fMemoryType == MEM_ALL && pMem->iDPMIPresent))
  1939.     {
  1940.       /* DPMI Titles */
  1941.       Qstrcat (pqszStrings[i], paszMemoryTitles[MT_DPMI_TITLE]);
  1942.       PrepNextString (pqszStrings, i++);
  1943.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1944.       /* DPMI Detected */
  1945.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_DPMI_DETECTED],
  1946.                    wAlignColumn);
  1947.       if (pMem->iDPMIPresent)
  1948.         Qstrcat (pqszStrings[i], pszYes);
  1949.       else
  1950.         Qstrcat (pqszStrings[i], pszNo);
  1951.       PrepNextString (pqszStrings, i++);
  1952.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1953.       /* DPMI Version */
  1954.       QstrcatAlign (pqszStrings[i], paszMemoryTitles[MT_DPMI_VERSION],
  1955.                    wAlignColumn);
  1956.       sprintf (chBuffer, "%d.%02d", pMem->iDPMIMajorVersion,
  1957.                pMem->iDPMIMinorVersion);
  1958.       Qstrcat (pqszStrings[i], chBuffer);
  1959.       PrepNextString (pqszStrings, i++);
  1960.       CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1961.     }
  1962.   /* Add the lines for the visual memory map down to the A000 region */
  1963.   if (i - 2 < MEM_640K_LIMIT)
  1964.     {
  1965.       for (; i - 2 < MEM_640K_LIMIT;)
  1966.         {
  1967.           PrepNextString (pqszStrings, i++);
  1968.           CopyVisualMemMap (pMem, pqszStrings[i], i, fOverlayFlag);
  1969.         }
  1970.     }
  1971.   else
  1972.     {
  1973.       PrepNextString (pqszStrings, i++);
  1974.       pqszStrings[i][0] = '';
  1975.     }
  1976.   /* Look for the last nonblank line */
  1977.   {
  1978.     BOOL fDone = FALSE;   /* Flag for dropping out of the loop */
  1979.     while (fDone == FALSE)
  1980.       {
  1981.         /* Strip extra spaces */
  1982.         PrepNextString (pqszStrings, i);
  1983.         /* Check to see if this line is blank */
  1984.         if (pqszStrings[i][0] == '' && i > 1)
  1985.           --i;
  1986.         else
  1987.           fDone = TRUE;
  1988.       }
  1989.   }
  1990.   /* Set the last pointer to NULL */
  1991.   pqszStrings[i + 1] = NULL;
  1992.   /* Return the pointer to pqszStrings */
  1993.   return (pqszStrings);
  1994. }
  1995. /*********************************************************************
  1996.  * CopyVisualMemMap - Copy the visual memory map to the string array.
  1997.  *
  1998.  * pMem         - Pointer to memory structure which contains the
  1999.  *                visual memory map.
  2000.  * pszString    - String upon which to copy the current line of the
  2001.  *                visual memory map.
  2002.  * i            - Current line number of the memory display strings.
  2003.  * fOverlayFlag - Places the overlay data (EMS Page frame, etc) onto
  2004.  *                the visual memory map, if TRUE.
  2005.  *
  2006.  * No return value
  2007.  *********************************************************************/
  2008. VOID CopyVisualMemMap (MEMORY_STRUCT *pMem,
  2009.                        QSZ  qszString,
  2010.                        WORD i,
  2011.                        BOOL fOverlayFlag)
  2012. {
  2013.   INT  iMapIndex;     /* Index to the visual memory map */
  2014.   CHAR chBuffer[80];  /* Local sprintf buffer           */
  2015.   WORD u;             /* Looping variable               */
  2016.   /* Make sure the string is blank */
  2017.   qszString[0] = '';
  2018.   /* Calculate the appropriate line of the visual mem map */
  2019.   /*   for this line in the display strings.              */
  2020.   iMapIndex = (NUM_OF_ROWS - 1) - (i - 2);
  2021.   if (i + 1 < MEM_640K_LIMIT)
  2022.     {
  2023.       /* Display decimal equivilent of memory location */
  2024.       if (i == 2)
  2025.         Qstrcpy (qszString, "1024K ");
  2026.       else if (iMapIndex % 4 == 0)
  2027.         {
  2028.           sprintf (chBuffer, "%4uK ",
  2029.                  (WORD) ((DWORD) iMapIndex * (DWORD) 0x4000 / (DWORD) 1024));
  2030.           Qstrcpy (qszString, chBuffer);
  2031.         }
  2032.       else
  2033.         Qstrcpy (qszString, "      ");
  2034.       /* Display hexadecimal equivilent of memory location */
  2035.       sprintf (chBuffer, "%04X ", (WORD) iMapIndex * 0x0400);
  2036.       Qstrcat (qszString, chBuffer);
  2037.       /* Put in memory map with the memory map overlay */
  2038.       Qstrcpy (chBuffer, pMem->abMemoryMap[iMapIndex]);
  2039.       for (u = 0; u < NUM_OF_COLS - 1; ++u)
  2040.         if (fOverlayFlag && pMem->abMemMapOverlay[iMapIndex][u])
  2041.           chBuffer[u] = pMem->abMemMapOverlay[iMapIndex][u];
  2042.       /* Add memory map string */
  2043.       if (fReportFlag == FALSE)
  2044.         Qstrcat (qszString, "&1");
  2045.       Qstrcat (qszString, chBuffer);
  2046.       if (fReportFlag == FALSE)
  2047.         Qstrcat (qszString, "&0");
  2048.       /* Display hexadecimal equivilent of memory location */
  2049.       sprintf (chBuffer, " %04X", (WORD) ((iMapIndex + 1) * 0x0400) - 1);
  2050.       Qstrcat (qszString, chBuffer);
  2051.       Qstrcat (qszString, "  ");
  2052.     }
  2053.   else
  2054.     {
  2055.       Qmemset (qszString, ' ', 34);
  2056.       qszString[34] = '';
  2057.     }
  2058. }