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.

user3840170
  • 23,072
  • 4
  • 91
  • 150
David Given
  • 1,330
  • 8
  • 20
  • DOS never defined a size to reserve. Any specific reason why not just giving a KiB or two? – Raffzahn Aug 18 '22 at 19:10
  • 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 '22 at 20:46
  • 1
    @ecm DOS touches fs and gs? Isn’t DOS supposed to run on an 8088? – user3840170 Aug 19 '22 at 07:40
  • OpenWatcom seems to work just fine with the default stack: https://github.com/open-watcom/open-watcom-v2/blob/7f7450c9871c328d5587ce00dc9c91e189ac2abe/bld/wattcp/src/wdpmi.c#L143. Are you sure you need to define your own? – user3840170 Aug 19 '22 at 07:52
  • int21 (Header) or int31 (text)? – UncleBod Aug 19 '22 at 08:22
  • 2
    @UncleBod the idea is to use interrupt 0x31 from protected mode to call interrupt 0x21 in real mode. – Stephen Kitt Aug 19 '22 at 08:23
  • @user3840170 I think DOS up to version 6.xx will save the 386 registers (high words and fs and gs) when running on a 386, detected at run time. Might be wrong though. I thought the FreeDOS kernel did this too but apparently it only saves them if it may use them itself (ie its C code is compiled to require a 386): https://github.com/FDOS/kernel/blob/7149a8efe49f9d33dbabfd280517c6da06086a73/hdr/stacks.inc#L132 – ecm Aug 19 '22 at 09:42
  • 1
    @user3840170 Just checked on https://www.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 '22 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. – Martin Rosenau Aug 19 '22 at 20:01
  • @MartinRosenau: Is that with or without the STACKS line in config.sys? – Joshua Aug 19 '22 at 22:24
  • @Joshua It is with stacks line. – Martin Rosenau Aug 20 '22 at 06:04
  • IIRC STACKS is processed at the end of CONFIG.SYS setup, so TSRs loaded after that don’t benefit anyway (I can’t remember if it’s used for 0x21). – Stephen Kitt Aug 20 '22 at 08:10

1 Answers1

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.

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
  • 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. – David Given Aug 20 '22 at 08: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). – Stephen Kitt Aug 20 '22 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 '22 at 10:33
  • 1
    I had also misremembered '30' as '20' and 'words' as 'bytes', so that didn't help... – David Given Aug 20 '22 at 10:56
  • @Raffzahn: I've had this discussion with some others about the Windows ABI. MS doesn't want to guarantee a maximum stack usage of kernel32 calls; but they in effect already have because existing applications that left enough for Windows 95 will crash if they now use more than the applications left. – Joshua Aug 20 '22 at 20:19
  • @Joshua Not really sure what Win32 ABI or Kernel32 has to do with DPMI environment for DOS calls. – Raffzahn Aug 20 '22 at 20:23
  • 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 '22 at 20:57