If I remember correctly, you could ask DOS to load in upper memory by writing something like...


...somewhere in the CONFIG.SYS file.

But the question is, how did DOS know what portions of the UMA where available? Were there any BIOS functions that would report this info? (I know there's a INT 0x15, EAX = 0xE820 for this, but it only works for 32-bit machines).

If so, what if wanted to write a TSR that would monitor BIOS interrupts even before DOS is loaded? Would the OS overwrite my code or do I have any way to "protect" my TSR memory from being claimed later?

  • 1
    I remember it as HIGH,UMB (or HI,UMB?) but that's just a tangential comment. I used to write TSR's and I don't recall them ever having the ability to utilize memory over 640k, at least with the tools I had (Turbo Pascal with third-party TSR tools) but I was able to use overlays with my TSR's so they had quite small footprints. – Bill Hileman Mar 13 '18 at 19:48
  • 1
    @BillHileman - HIGH and UMB control different things, although you'd usually want either both (for maximum memory) or neither (for maximum compatibility): HIGH makes the area between FFFF:0010 and FFFF:FFFF available. UMB makes any EMS memory blocks in the range A000:0000 to FFFF:000F available. DOS would have been able to load your TSRs into a UMB if you ran them via the LOADHIGH command, and unless you made memory address assumptions about being in the regular 640K area (which I don't think you could do with TP) this would have worked. – Jules Mar 15 '18 at 9:02
  • PC DOS 7.0 could do wonders with putting things in high memory (even rebooted to see if the chosen configurations worked one at a time). – Thorbjørn Ravn Andersen Dec 2 '19 at 22:44

When configured to use upper memory (DOS=UMB) or high memory (DOS=HIGH) or both, the DOS “kernel” doesn’t find that memory itself; it relies on the services of a memory manager such as HIMEM.SYS, and the (XMS) APIs it provides. The memory manager determines what memory is available using a variety of techniques.

However, what you’re trying to do doesn’t require messing around with that behaviour. To hook BIOS interrupts and monitor them before the operating system starts, you’d effectively need to write something like a boot sector virus. The technique there is to position your code somewhere at the top of memory, then “hide” by altering the memory information so that the portion you’re using is no longer considered to be part of the available memory. On an 8086-class device, you would call interrupt 12h to determine how much memory is installed (in KiB), then hook that and return the amount reduced by however much you need — or better, change the value stored in a word in the BIOS information pages in segment 40h at offset 13h, which is used by the interrupt 12h handler. On a 286-class device with extended memory, you would hook interrupt 15h function 88h and sit at the top of extended memory. On a 386-class device, you could use V86 mode instead and then have some real fun — that’s how QEMM for example was able to implement its Stealth memory management and its fast boot option which re-initialised DOS without going through the BIOS at all. Once you’ve implemented any of these options, you can boot DOS after loading your “virus” and it won’t touch the memory you’re using.

Note that using upper memory (between 640KiB and 1MiB) without a memory manager is rather complicated: there are no guarantees that memory is actually mapped there. Typical 386+ DOS installations use EMM386 or a similar memory manager to map memory using the 386’s paging features (in V86 mode). Another approach, which works with 286-class systems too, is to use a chipset-specific driver such as UMBPCI (on Pentium-class systems) or a chipset-aware memory manager such as The Last Byte Memory Manager; these generally work by enabling “shadow memory” and using it for UMBs. So in your case you really are limited to using the top of either conventional memory or extended memory.

If you’re curious about the inner workings of all this, check out the memory managers in FreeDOS. For more advanced exploration of DOS and even Windows, I highly recommend 386SWAT which is a very sophisticated debugger. System startup, and the hoops DOS has to jump through to support the various low/UMB/high configurations it can encounter, are described in detail in chapter three of Geoff Chappell’s DOS Internals (and memory management in general occupies a significant portion of the book as a whole).

| improve this answer | |
  • 1
    "When configured to use upper memory (DOS=UMB) or high memory (DOS=HIGH) or both, DOS doesn’t manage that memory itself;" Erm, no. Ofc, DOS manages that memory. - After it aquired the available blocks for a driver. – Raffzahn Mar 14 '18 at 1:44
  • 1
    @Trap my whole point is that you don’t need to depend on HIMEM.SYS or any other memory manager to do what you want; since you’re loading earlier, you “steal” memory by changing what the BIOS reports is available. As I mentioned, hooking interrupt 15h allows you to use extended memory (in more detail, services 88h, 7Ch or E820h depending on the system’s vintage). – Stephen Kitt Mar 14 '18 at 5:21
  • 2
    @Raffzahn, of course DOS manages it once it’s allocated it, but it uses the XMS UMA and HMA APIs to allocate it initially, so it’s not its responsibility to determine what’s available in the system’s memory map. (In relation to “how did DOS know what portions of the UMA where available?”) – Stephen Kitt Mar 14 '18 at 5:24
  • 1
    @Jules I wondered how long it would take for someone to point that out ;-). TBH I didn’t think it was worth going into detail on the memory management side of things because it doesn’t seem particularly relevant given what the OP is trying to do. I’ll clarify that I’m talking about the DOS “kernel”... – Stephen Kitt Mar 15 '18 at 9:45
  • 1
    I wrote actual code in the way @StephenKitt wrote that lived in the MBR and it worked well with DOS and Windows 3.x. – Rui F Ribeiro Mar 16 '18 at 0:13


DOS itself doesn't touch neither High-Mem (HMA) nor UMB without being ordered to do so. And even then it needs a driver capturing and providing these memory areas. Usually this is HIMEM.SYS.


Well, it can't be a standard TSR, as there is no DOS to load it. But yeah, as soon as there is memory above 640KiB it can be used to store a program surviving a restart and monitor activity before (and during) DOS load.


No, unless of course, you have it use HIMEM.SYS, which in turn would not hesitate, but just grab whatever is available.

The first task is to find some memory. HMA might seem like a good idea, but as said before, then HIMEM.SYS and DOS=HIGH can't be used at all. Better use a CGA as display adaptor, while installing some spare MDA (or vice versa), as they use different address areas in Upper Memory. OR even better, if at hand, use some (PC) memory card that allows the addition of RAM anywhere in the address space.

Now boot into DOS and start your 'special' TSR, which moves itself up into one of these additional RAM areas. The real work will be to find a combination of trigger that allows your code to keep itself in the loop during all stages of reset and boot.

If you found a way, it's all set to boot again and spy on DOS :)

| improve this answer | |
  • 1
    Isn't DOS the one that load drivers? In BIOS world there does not exist such a thing as a file – Trap Mar 13 '18 at 22:45
  • Sure, DOS loads the drivers and then uses whatever they provide. – Raffzahn Mar 13 '18 at 22:49
  • I tried to run some code from video display memory (B800h or A000h) and it crashed the computer right after the first instruction. – Trap Mar 13 '18 at 22:52
  • For one, does your machine have memory there? And second, do not use the memory of the active display, as already the scrolling when pressing enter wil ldetroy your programm. – Raffzahn Mar 14 '18 at 1:41
  • 1
    @Trap - crashing while running from display memory may deserve it's own question, but a relevant factor is what kind of system and what kind of display hardware do you have? If you have a 16-bit bus and an 8-bit graphics card or a 32-bit bus and a 16-bit graphics card, it could be that the processor is only getting half of a memory fetch fulfilled and that's causing it to execute junk instructions (likely 0xFFFF, which I think is an invalid opcode, so should cause a trap on 286 or above). – Jules Mar 15 '18 at 9:17

Real-mode MS-DOS didn't care where BIOS and expansion card memory locations were and really didn't have to because it generally made use of the interrupt system for system calls. EMM386 had to do its own detective work to detect memory: note that the documentation for INT 15h AX=E820h has many caveats about what memory it shows, and I don't think that it couldn't be relied on to exist on every machine when MS-DOS 6.22 came out. INT 12h/address 0:413h was used to establish its memory map, which was correct unless something like EMM386 came afterwards to to backfill memory or extend it (which you could do if you didn't use VGA: the A0000h-AFFFFh range could be mapped and added to contiguous conventional RAM, or A0000h-B7FFFh if you only used monochrome text.)

IIRC, EMM386 used the same system that the BIOS did to locate memory: walk through physical addresses C8000h-F5FFFh (see step 27 of the link) and find a ROM signature and length. You could override this by adding explicit I=xxxx-yyyy and X=xxxx-yyyy switches to include and exclude memory, and use HIGHSCAN to use a more aggressive scan, but this was the general method.

If you wanted to run a program earlier in the boot process than DOS, you would have to alter the boot sector so that you could start before IO.SYS (@Stephen Kitt's answer details this.) The INT 12h method used by programs like hard disk manager drivers and (sadly) viruses continued to work because EMM386 had to honour the system configuration prior to going into protected mode and keep the memory map looking the same as much as possible: this is important as MS-DOS (and the expansion cards as well) were written to run in real mode and relied on memory addresses not to change.

| improve this answer | |
  • So, do you think it'd be possible to append some fake ROM signature in the range from C8000h to F5FFF and put there the code? – Trap Mar 14 '18 at 14:14
  • @Trap it would, if you have memory to write to in the ROM range (and that’s a big if). – Stephen Kitt Mar 14 '18 at 14:59
  • If one had a hard drive controller that needed to grab e.g. 1024 bytes from the top of RAM before it started, would MS-DOS then recognize the existence of usable memory from 0x00000 to 0x9FBFF and 0xA0000 to 0xAFFFF, with a hole from 0x9FC00 to 0x9FFFF? – supercat Mar 14 '18 at 16:06
  • @supercat I think that's how it worked when you had a drive overlay installed: It's been a long time since I had to use Disk Manager! By the time that EMM386 became widely used, everyone I know had VGA-compatible video cards, so the theoretical ability to use the A block was moot anyways. – ErikF Mar 14 '18 at 18:48
  • 2
    @Trap - if your system is targetting 386+ processors, one approach would be to switch to V86 mode preemptively, and then provide your own implementation of EMS, effectively entirely replacing EMM386. I think that if the system is already in V86 mode when EMM386 tries to start it'll just fail to load and then everything will continue as normal. There's an implementation of EMM386 in the FreeDOS source code that you could use as a starting point. – Jules Mar 15 '18 at 9:31

DOS knows what memory is available because it requests this information from whatever upper memory manager you have installed via an XMS function call.

For example, EMM386 can be used to simulate upper memory using XMS memory (if you have an 80386 CPU or higher), and if it is doing so then it will also provide the needed XMS API functions to allow upper memory blocks to be requested.

The method used to communicate is via a particular interrupt function established by HIMEM.SYS (or a driver that provides similar functionality). HIMEM.SYS itself manages only the high memory area, for upper memory all it does is allow an upper memory manager to register itself via a hook so that it may recognize and respond to upper memory requests to request/release blocks as needed (via XMS functions 10h and 11h). For details consult the Extended Memory Specification, version 2.0 or higher (an Internet search for "xms20.txt" can be used to find this document).

| improve this answer | |

Your Answer

