• Please review our updated Terms and Rules here

MS-DOS 16-bit: can malloc() and _fmalloc() be mixed?

reijo

New Member
Joined
Oct 27, 2022
Messages
4
I'm not experienced with 16-bit realmode programming, so I'm sorry if this is an obvious problem. I'm not sure if I've hit some bug, or this is normal.

Anyway, the main question is: is it possible to mix malloc()+free() and _fmalloc()+_ffree() calls in one application? So e.g. normally I use malloc()+free(), but for larger data blocks and selected far pointers I'd use _fmalloc()+_ffree()?

I'm not sure because of this behavior of OpenWatcom's 16-bit compiler (compilation using simply "wcl test.cpp"):

Code:
#include <malloc.h>
#include <stdio.h>

int main() {
   printf("%08lX\n", _fmalloc(2000));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   return 0;
}

That displays:

1692389450280.png

So, every malloc() call I'm doing after _fmalloc() seems to fail. But when I do a malloc() first, THEN _fmalloc(), then everything seems to be fine:

Code:
#include <malloc.h>
#include <stdio.h>

int main() {
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", _fmalloc(2000));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   printf("%08lX\n", malloc(0x50));
   return 0;
}

1692389565487.png

So I'm wondering, if this is some bug, undefined behavior, a logic consequence of something I'm not aware of? I've actually checked the same code with Borland C++ v3.1, and using default settings ("bcc test.cpp") it seems that there's no such problem there -- allocation via malloc() always succeeds (for such small test program at least).

(Also, thread topic fix: it should be "DOS", not "MS-DOS")
 
Last edited:
Here is the simplified version.

You may have up to two heaps - a near one and a far one. "malloc" refers to either the near malloc or far malloc functions by default, depending on your memory model. Memory models supporting just 1 data segment are probably using the near versions, while the other memory models supporting more than one data segment use the far versions.

This depends on your compiler runtime, but Watcom lets you explicitly use the near or far versions of the malloc functions no matter what memory model you are using. So you can specify far malloc calls even when using the small memory model. See your compiler reference for details.
 
I've done some testing with this and mbbrutman is spot on.. There can be a local heap of 64K and a far heap with remaining conventional memory 400K+. It seemed like it affected certain memory models, but I don't remember which ones.

You can make a test application that uses each one and checks coreleft() and farcoreleft() to see which heap it allocated to.

edit: The top is compiled as small model, the bottom is compiled as large model. Each allocates 32000 bytes via malloc and farmalloc and then shows with coreleft / farcoreleft the changes:

top one has two heaps - you can see how it split the memory (small model) compared to the bottom which has a single larger heap (large model).

1692411730022.png

Code:
#include <stdio.h>
#include <alloc.h>

int main()
{
  char *p;
  char far *fp;

  printf("heap %lu, farheap %lu\n", (unsigned long)coreleft(), (unsigned long)farcoreleft());

  p=(char*)malloc(32000);
  fp=(char far*)farmalloc(32000);

  printf("heap %lu, farheap %lu\n", (unsigned long)coreleft(), (unsigned long)farcoreleft());

  if (fp!=NULL)
    farfree(fp);
  if (p!=NULL)
    free(p);

  return 0;
}
 
Last edited:
Your printf() test harness may also need the near malloc() return values cast to (long) if you're going to do that. Being a varargs function, printf() uses the K&R type promotion rules which are just hard to think about, but your near pointer from malloc may only be getting passed as a 16-bit value, while printf() is expecting a 32-bit value to be passed to it on the stack.

Not sure if watcom has a diagnostic built in to try and compare printf format strings with what you're passing and generate a warning if they don't match. Normal type checking can't catch this so it requires a special feature for the *f functions.
 
But back to the OP's original question. Does calling _fmalloc() before ever calling malloc() cause _fmalloc() grab all the space for itself that might otherwise have been available for the near-heap had at least one call to malloc() been made first?
 
I think this is actually expected. From the Library Reference:
The _nheapgrow function attempts to grow the near heap to the maximum size of 64K. You will
want to do this in the small data models if you are using both malloc and _fmalloc or halloc.
Once a call to _fmalloc or halloc has been made, you may not be able to allocate any memory
with malloc unless space has been reserved for the near heap using either malloc, sbrk or
_nheapgrow.
 
I suspect that it depends on the memory model. You can get a definitive answer for your particular run-time by monitoring calls to DOS API 48h.
The common-sense approach might be to realloc (DOS API 4ah) the entire 64K near segment (isn't that the default for the tiny model), then use 48h to perform your _fmalloc calls.
Don't most C runtimes publish their startup code? I know that MSC does--and I've written my own startup code for that.
 
I am doing a fair bit of Borland Turbo C++ 3.0 programming at the moment. There, Borland's malloc() and farmalloc() allocations can freely be mixed, although that would definitely be a property of the compiler runtime and not of DOS, so not sure if my observation with Borland helps anything with Watcom.

However I want to echo Makefile's point: the printf() width specifiers in the printf() are wrong, and that could well be a source of odd cryptic issues like that.
In Borland's runtime at least, pointers that are implicit in memory model (could either be near or far) are printed with "%p", explicitly near pointers are printed with "%Np", and explicitly far and huge pointers are printed with "%Fp". (Same applies to strings for example, if you have a far string, that would be printed with "%Fs")

Not sure how much standardization printf() format specifiers had at that time, so double check if Watcom's docs have any specific mention about that. Otherwise, trying to cast the pointer to an unsigned long could be a try. (although on Borland's side, I find that casting near or far pointers to unsigned longs behaves oddly, better use Borland's FP_OFF() and FP_SEG macros from its DOS.H)
 
Hey btw, I hope I don't derail the thread too much, but if you are doing far memory allocations in Watcom, it would be interesting if you can find an answer to the question of how to allocate a given amount of far memory that is guaranteed to not straddle a 64KB alignment mark? This kind of allocation is needed for DOS Sound Blaster DMA programming. Check out https://github.com/juj/64KALLOC for motivation and how I did it in Borland Turbo C++ 3.0. Basically Borland did not have any built-in functions for it, so I ended up "abusing" the provided allocator by doing a sequence probing allocations. I wonder if that program would work on Watcom.
 
Your printf() test harness may also need the near malloc() return values cast to (long) if you're going to do that. Being a varargs function, printf() uses the K&R type promotion rules which are just hard to think about, but your near pointer from malloc may only be getting passed as a 16-bit value, while printf() is expecting a 32-bit value to be passed to it on the stack.
I intentionally cast coreleft() to unsigned long because it returns unsigned int under the small memory model and unsigned long under the large memory model, that way the same code could be used under either model.
 
Back
Top