20

The context is that I'm calling DOS interrupts from DPMI using int31 / ax=0x300, and you have to tell it what stack you want the real mode call to have. By default you get a small stack of about 20 bytes which isn't nearly enough. Experimentally I've found that 128 bytes is still too small and 512 seems to work, but it'd be nice to have a definitive source as to what it should be.

12
  • 4
    DOS itself (its interrupt 21h handler) typically saves 7 16-bit word GPRs, two segment registers, the 6-byte interrupt stack frame, plus 8 (or 7?) GPR high words and fs and gs on the caller stack. That's a total of less than 64 bytes. A few interrupt 21h functions are handled on the user stack however. And resident software may add additional needs. Oh, the code segments of modern DOS kernels are typically located in the HMA, which may require an XMS driver call if A20 is currently disabled, and XMS is documented as needing at least 256 bytes of stack space.
    – ecm
    Aug 18, 2022 at 20:46
  • 1
    @ecm DOS touches fs and gs? Isn’t DOS supposed to run on an 8088? Aug 19, 2022 at 7:40
  • 2
    @UncleBod the idea is to use interrupt 0x31 from protected mode to call interrupt 0x21 in real mode. Aug 19, 2022 at 8:23
  • 1
    @user3840170 Just checked on pcjs.org/blog/2015/02/22 running the MS-DOS 6.22 kernel and using Enhanced Debug to trace into interrupt 21h. It does not save any 386-specific registers. MS-DOS 7.10 however, which requires a 386, does save the high words of eax, ebx, ecx, edx, and edi. However, they're saved on DOS's stack, not on the application's stack.
    – ecm
    Aug 19, 2022 at 10:18
  • 1
    I just called an int21 interrupt under DOS 6.22. 90 bytes were used. Normally, this should not differ for different functions because DOS has its own stack and modifies the SS:SP registers when entering the interrupt. However, some loadable drivers may overwrite int21, push data on the stack and call the original int21. If you have multiple of these drivers, the stack consumption may increase a lot. Aug 19, 2022 at 20:01

1 Answer 1

17

I’m not aware of any official documentation of MS-DOS’s stack requirements for interrupt 0x21; in any case, that wouldn’t paint the full picture since any TSR or driver hooking the interrupt could add to the required stack size without providing any means of determining how much extra room it required.

However the DPMI 1.0 specification says the following in its section on stacks and mode-switching (also available on DJ Delorie’s site):

The real mode stack is also provided by the DPMI host, and is usually located in the DPMI host data area allocated by the client prior to its initial switch into protected mode. The real mode stack is at least 200H bytes in size and is always located in locked memory. Interrupts that are reflected to real mode, as well as calls to real mode interrupt handlers or procedures via Int 31H Functions 0300H, 0301H, or 0302H, will use this stack.

So as far as the DPMI specification is concerned, the answer is 512 bytes. This was already the case in version 0.9 of the specification:

The DPMI host will provide the client with a real mode stack that is at least 200h bytes in size and will always be locked. Interrupts that are reflected into real mode, as well as calls made using the translation services, will be reflected on this stack. DPMI hosts will not automatically switch stacks for hardware interrupt processing in real mode since DOS performs this function automatically.

Your findings suggest that at least some DPMI implementations provide a (much) smaller stack; Ralf Brown’s Interrupt List agrees, saying “DPMI will provide a small (30 words) real mode stack if SS:SP is zero”. I imagine there’s a reason behind the 512-byte minimum in the specification, so I take this as confirmation of your findings: the default stack can’t be relied upon (whatever the specification says), but having the caller provide a 512-byte stack should be sufficient.

Typically you’d use a DOS extender which would provide a protected-mode interrupt 0x21 handler and take care of all this for you, but presumably that’s not applicable here.

7
  • 2
    Interesting; I was going with Ralf Brown's Interrupt List, which says this about int 31 0300: DPMI will provide a small (30 words) real mode stack if SS:SP is zero. I was definitely observing stack corruption with the default stack and switching to a larger stack made it go away. Aug 20, 2022 at 8:33
  • 2
    Yes, I gathered from your question that some implementations don’t provide as much as the spec says! The purpose of my answer wasn’t to suggest that you should rely on the default; rather, that since the spec relies on a 512-byte stack (or larger), presumably there’s a reason for that and you should provide a 512-byte stack if you’re going to provide your own (which also matches your findings). Aug 20, 2022 at 10:15
  • 1
    It might be helpful, that DOS simply can not guarantee any maximum stack usage, as DOS relies on external drivers, for all devices, which it has no control over - at least not in terms of stack usage.
    – Raffzahn
    Aug 20, 2022 at 10:33
  • 1
    I had also misremembered '30' as '20' and 'words' as 'bytes', so that didn't help... Aug 20, 2022 at 10:56
  • 1
    @Raffzahn: Same basic problem. If you don't define a value; the value will be set to some arbitrary value by pre-existing application code and later drivers won't work.
    – Joshua
    Aug 20, 2022 at 20:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .