Despite the brilliant post from @GoingDown, I'm going to attempt to explain a bit what's going on with EMM386.EXE:
EMM386.EXE, when used with the parameter NOEMS, will only be used as a memory manager, this means, it can put resident programs in the space between the 640KB of basic memory and 1MB, known as UMA (upper memory area). 1MB is the maximum RAM addressable by the Intel 8088 (and 8086) so it's an architectural limit (*), yes Bill Gates said 640KB was enough and how wrong he was, but he didn't really have much more to play with, and those extra 384KB were really important for I/O to be mapped and being future-proofed for future expansion cards, so in theory, programs must avoid using it for their own purposes as things will break when doing it.
The PC compatibles of the time tried to keep the UMA mapping more or less similar to the IBM PC, so there are ranges universally available and others that can be stepped into because they are not in use, like the monochrome graphics.
The parameter NOEMS
means we just want the functionality that puts TSRs ("terminate and stay resident" programs) on the upper memory area, but doesn't provide EMS RAM. This is the best option to helps getting as much basic memory (under 640KB) and requires to use LOADHIGH and DEVICEHIGH for the OS to move the TSRs to the UMA.
EMS is a different animal, it's just a technology to present the the RAM beyond 1MB (extended memory) to programs, using memory bank switching of a small chunk of contiguous UMA RAM, usually 64KB, so it can address several MB.
This means another 64KB of UMA are needed, out of those 384KB, and these have to be contiguous, so if many drivers and TSRs are running, these have to be arranged carefully to allow for the 64KB to fit in one chunk.
You could have the case of TSRs having to be loaded under the 640KB mark despite having lots of UMA available simply because the free UMA is fragmented (imagine multiple gaps of 10KB but your TSR needs 15KB). Hence why NOEMS can help, you can send drivers to the UMA but you don't use those 64KB that EMS needs, giving even more space for TSRs.
An alternative that sometimes helps is something like MemMaker, which can use parameters to put residents in different addresses without worrying about the order in which they are loaded, and calculates the best order to leave the biggest ranges available.
In ao486 the UMA is not like in most PCs of the time, things are not where EMM386.EXE or Qemm expect, an agressive scanning of the UMA produces crashes in most cases and that's why adding an exception for "a000-c7ff" is necessary. On top of that, there is simply no 64KB of contiguous address space in the upper memory, even after including "c800-efff", so the page frame size has to be reduced to 56KB (e000 hexadecimal is 57344 decimal). This will already cause compatibility issues with some programs that expect to use the full 64KB frame size.
I must add, I haven't used the settings suggested by @GoingDown but I'll definitelly take note of those as they seem quite polished (I was using a 48KB frame), but I have experienced quite strange things when using memory managers, like corrupted floppy I/O and crashes on some games, so I would keep low expectations of stability with software that needs EMS.
Also, please notice that sometimes people say "oh, this program is not compatible with EMS" the issue generally is that the program is not compatible with memory managers, and even using the parameter NOEMS will still cause issues with games like Ultima VII. For those, I recommend to investigate the usage of UMBPCI
, although it will probably not work on ao486.
Also, even if a game doesn't require EMS, using EMM386 is beneficial to maximise basic memory as it allows to "loadhigh".
*Note: Since the 80286 the RAM beyond 1MB is usable, but only in protected mode, which DOS had challenges to support due to compatibility with legacy software. Turns out that programmers in the 8086 era would just use the RAM addressing limit to wrap around the address space and reach low memory areas (imagine expecting to go from 999 to 0 by adding 1, but instead you end up in 1000: things are going to break). It's still possible to use protected mode in DOS, thanks to solution like DOS4GW, but MS never bothered supporting it as it wash pushing other alternatives like NT.