This is a snapshot of my ongoing work on MMM386, a 386 paging memory manager
that greatly surpasses EMM386, QEMM-386, and 386Max while being 5 times smaller
and totally free with complete source code.

MMM386.EXE is the only executable. It can be installed from the command line,
AUTOEXEC.BAT, or CONFIG.SYS, it doesn't care. Eventually I'll write the user's
manual and the programmer's manual, but for now the terse description of the
command line switches and the blurb on VCPI below is all I have.

The *.ASM files are the sources and MAKEFILE is, well, the makefile. How
surprising...

                                *** WARNING ***

This is a snapshot of ongoing development work. You can read the sources for
your edification, but be warned that many internal interfaces are certain to
change significantly in the eventual release.

Development Snapshot History:

SNAP1 Original.

SNAP2 Many changes, can't remember all.

SNAP3 Modified the stopgap kludge in GEMMIMP (BASE.ASM) so that it uses the
      actual EMS page frame address instead of hard-coded D000. This makes the
      experimental Win386 Paging Import support work with /U. The side effect,
      however, is that if you use /N, the Paging Import structure will show the
      page frame at 0000. Experiment shows that Win386 still runs in this
      horrible configuration and most of V86MMGR functionality appears to work,
      but don't even try to run any LIM EMS apps in a VM in this configuration.

SNAP4 Moved PhysPageAlloc and PhysPageFree out of the overcrowded BASE.ASM into
      a new module (PHYSPAGE.ASM). Modified PhysPageAlloc to remember the last
      allocated page and start looking for new pages after that point. This
      makes the real-life performance of PageUtilFillRegWithLockedPages and
      PageUtilFillRegWithUnlockedPages more of O(n) instead of O(n^2). Turned
      out to be significant on machines with huge memory, even the super-fast
      ones. Also fixed the bug with /B and /X switches allowing zero or
      ridiculously small values.

Command Line Switches:

/A:n Controls the high linear address space size by setting the number of page
     tables to n. Each page table occupies 4 KB of extended memory and provides
     4 MB of high linear address space. At least one page table is required,
     and the maximum is 512 (2 GB of high linear address space). The default is
     chosen so that the high linear address space is 4 times larger than the
     physical memory, unless it would exceed 2 GB, in which case 512 page
     tables are created (2 GB of high linear address space, the theoretical
     limit). The Intel 80386 architecture does not allow partial page tables,
     so the high linear address space is always a multiple of 4 MB.

/B:n Sets the number of available high linear memory block handles to n. Each
     handle occupies 4 bytes of extended memory. The default is 1024, the
     minimum allowed is 256, and the maximum supported is 32768.

/F:xxxx Sets the EMS page frame paragraph address to xxxx. The address must be
        expressed as 4 hex digits. (Although LIM EMS is not implemented yet,
        the page frame allocation code is already there, hence this switch.)
        Since LIM EMS is not implemented yet, this switch has very little
        effect. The experimental Win386 Paging Import code, however, uses this
        value, so whatever page frame address you choose will be the one seen
        by LIM EMS clients in Win386 VMs. See the GEMMIMP routine in BASE.ASM.

/H:n Sets the minimum HMA allocation to n KB (equivalent to HIMEM's /HMAMIN=n).

/N Puts MMM386 in the no-frame mode. (Although LIM EMS is not implemented yet,
   the page frame allocation code is already there, hence this switch.) Since
   LIM EMS is not implemented yet, you would normally use this switch together
   with /U to increase the UMB size. Note the implications of this switch for
   Win386 support, however. See the SNAP3 entry in the development snapshot
   history above and the GEMMIMP routine in BASE.ASM.

/P:n Forces the physical extended memory size to n pages. Here I mean Intel
     80386 architecture pages, not LIM EMS pages. One page is 4 KB. Normally
     MMM386 determines this automatically using the standard BIOS function, but
     if you have memory that is not reported by this function (such as memory
     beyond 64 MB), you have to force this value manually. MMM386 supports
     contiguous extended memory only and the maximum size is 523998 pages
     (slightly less than 2 GB). These restrictions are inherent in the way
     MMM386 lays out the linear address space. Example: Suppose you have 127 MB
     of contiguous extended memory. Since there are 256 pages in 1 MB, you have
     to specify /P:32512 (32512=127*256).

/S:n Sets the Ring 0 stack size to n KB. n must be between 2 and 32. The
     default is 8 KB.

/U Enables UMBs.

/V Disables the check for Phar Lap in the VCPI presence check function. See the
   blurb on VCPI below.

/X:n Sets the number of available XMS EMB handles to n. Each handle occupies 12
     bytes of extended memory. The default is 64, the minimum allowed is 32,
     and the maximum supported is 255.

VCPI Support Note

MMM386 supports VCPI. However, the VCPI spec's addressing of the PIC mapping
issue is ambiguous. The spec says:

"Client programs
should query the server during initialization to determine the current
mappings. If they have been changed from the PC default mappings, the client
is obligated not to change them again.  If they are still set to the PC
defaults, the client can reprogram the interrupt controller and then inform
the server what the new mappings are."

While it says that if the PICs are already remapped, the client is not allowed
to remap them again, it doesn't say what exactly it is supposed to do, namely,
how should it reflect interrupts occurring in protected mode under its control
and how should it hook V86 mode interrupts occurring under the server's
control.

MMM386 remaps the PICs, but it appears that no other memory manager does. Since
the popular memory managers don't remap the PICs, the authors of DOS extenders
have never been able to test their extenders under this condition. When I
started testing different DOS extenders under MMM386, I found that their
interpretation of the part of the VCPI spec in question is different from mine.

Since obviously all standard PC software expects the PICs to be mapped at 08 &
70, a memory manager that remaps them has to emulate this in its Ring 0
interrupt reflection handlers. For example, MMM386 remaps the PICs to 50 & 70.
When a timer interrupt (IRQ 0) occurs, the CPU executes interrupt 50 (looks up
entry 50 in the IDT). However, MMM386's Ring 0 handler for interrupt 50
reflects it to V86 mode as interrupt 08, since that's what all standard PC
software expects.

However, the writers of DOS extenders apparently didn't realize this, and they
couldn't figure it out experimentally because apparently no other memory
manager remaps the PICs. Most DOS extenders check the current PIC mappings,
discover that they are 50 & 70, and continue with their initialization, setting
up the appropriate entries in their IDTs. Then when a hardware interrupt occurs
in protected mode under the extender's control, the extender's handlers make
the VCPI call to switch back to the server's V86 environment and then reflect
the interrupt to V86 mode. The problem is that most of the DOS extenders I have
examined reflect the interrupts using the remapped vectors, i.e., use entries
50-57 or whatever in the V86 IVT. The result is that control is transferred to
whatever garbage these IVT entries point to, and the final intended IRQ
handlers never gain control. No good!

Now, technically speaking, a PIC-remapping memory manager could be implemented
differently. One could have the Ring 0 handlers reflect the interrupts using
entries 50-57 of the V86 IVT and put V86 handlers on these vectors which
transfer control to the real handlers. This approach would be compatible with
the popular DOS extenders. However, the VCPI spec does not stipulate one way or
the other, meaning that MMM386 does not violate the spec and that the spec is
simply ambiguous.

What does this mean for MMM386 users? Although I could have redesigned MMM386
to be compatible with the way the popular DOS extenders interpret the ambiguous
VCPI spec, I have chosen not to do so for good technical reasons (speed,
modularity, flexibility, and transparency to standard PC software). As a
result, MMM386 is not compatible via VCPI with a lot of popular DOS extenders,
including Rational's DOS/4G, Phar Lap's 286 and 386 DOS extenders, and Charles
W. Sandmann's CWSDPMI, which ships with DJ Delorie's DJGPP v2.xx (a free 32-bit
protected-mode C/C++ compiler for DOS). However, just because it is not
compatible via VCPI does not mean that it is not compatible at all. All of the
extenders listed above support DPMI in addition to VCPI. I know of at least
stand-alone DPMI implementation for DOS which itself uses VCPI in a way that's
fully compatible with MMM386. If both MMM386 and a DPMI implementation of this
type are installed, DOS-extended apps will run correctly out of the box. They
will check for DPMI first, detect the DPMI implementation, and use it
thereafter for all mode switching. The DPMI implementation itself uses VCPI in
a manner compatible with MMM386 and therefore everything works beautifully.

The only problem area is with Phar Lap's extenders, which choose VCPI when
presented with a choice of both VCPI and DPMI. In order to make Phar Lap's
extenders work with MMM386, I have introduced a hack. MMM386 detects when a
Phar Lap DOS extender starts up and disables VCPI while its running, forcing it
to use DPMI instead. This hack can be disabled with the /V switch, but I don't
recommend doing this. This problem is for Phar Lap only. Rational's DOS/4G and
DJ Delorie's DJGPP v2.xx both check for DPMI first and always use DPMI when
it's available. Since Ergo's latest DOS extenders appear to be heavily based on
DPMI, I assume that they do the same, although I haven't been able to find one
for testing.
