I'm using Turbo C and Turbo Assembler 2.01 to write a C wrapper around the XMS interface so I can use XMS memory in real mode, large model. I've started by writing the following:

#include <dos.h>

void (*xms_driver_interface)();

   Query whether there is an XMS driver available.       
   Returns 1 if XMS memory is available.
int xms_available()
   union REGS inregs, outregs;       
   inregs.x.ax = 0x4300u;       
   int86(0x2Fu, &inregs, &outregs);       
   return !!(outregs.h.al & 0x80u);

   Query whether there is an XMS driver available,
   and populate xms_driver_interface if so.       
   Returns 1 if XMS memory is available and ready.
int xms_init()
   union REGS inregs, outregs;
   struct SREGS sregs;
   int success;
   inregs.x.ax = 0x4310u;       
   int86x(0x2Fu, &inregs, &outregs, &sregs);       
   success = outregs.h.al;       
   if (!success) return 0;
   xms_driver_interface = (void (*)())((((unsigned long int)sregs.es) << 4) + ((unsigned long int)outregs.x.bx));   

   return 1;

unsigned int xms_get_version_number()
   _AX = 0x0000;
   asm call [xms_driver_interface]
   return _AX;

When Turbo C reaches the line asm call [xms_driver_interface], it backs up and says it's Restarting compile using assembly, and switches to a two stage process where TCC compiles xms.c and TASM assembles it. TASM aborts with the error xms.ASM(111) Forward reference needs override, which seems like the output of the Turbo tools aren't mutually compatible because the generated xms.ASM is referring to _xms_driver_interface before declaring it, requiring two passes.

Is there a way I can tell Turbo Assembler through Turbo C to use multiple passes on assembling?

I know I can pass /m2 to TASM directly, but when I split up my compilation process into separate phases it doesn't link correctly and it turns into a big mess.

Or is there some other method I should use to invoke my calculated pointer?

3 Answers 3


Or is there some other method I should use to invoke my calculated pointer?

See Borland’s note on the subject: if you declare your function pointer as

void far (*xms_driver_interface) (void);

you can call it directly:


The note also shows how to construct the pointer in idiomatic Turbo C:

xms_driver_interface = (void (far *)(void)) MK_FP(sregs.es, outregs.x.bx);

or better yet,

_AX = 0x4310;
xms_driver_interface = (void (far *)(void)) MK_FP(_ES,_BX);

An other possible approach, which is recommended in general when accessing C symbols from inline assembly (see chapter 7 of the Turbo Assembler 2.0 user’s guide), is to specify the symbol size (which is what the assembler needs to know, and determines using two passes otherwise):

asm call dword ptr xms_driver_interface

If you want to stick with inline assembly, you should add a #pragma inline at the start of your C file; this will restart TCC straight away in “compile using assembly” mode.

  • 1
    The segment shift of 4 is not correct. A far pointer has segment:offset pair, each is a 16-bit word.
    – Justme
    Sep 22, 2020 at 8:55
  • Accepting this as the Users Guide is clear on this specific subject, but only if you're familiar with the terminology and know what you're looking for (which I'm not :) ): "However, it may be necessary to include appropriate WORD PTR, BYTE PTR, or other size overrides on assembly instruction. A DWORD PTR override is needed on LES or indirect far call instructions." page 280. This is the override the error message I quoted mentions.
    – knol
    Sep 22, 2020 at 9:39
  • Yes, I used to be intimately familiar with this, so reading the manual brings things back quickly ;-). When working with assembly and C in the Turbo environments, it’s important to realise that the integration on the C side is really basic, and the compiler won’t do much at all to help you. Things got better at some point in Borland C++, but I’m not sure when (3 or 4). Sep 22, 2020 at 9:44
  • An additional tidbit, #pragma inline is equivalent to the tcc -B option if you happen to be compiling on the command line or via a MAKEFILE.
    – smitelli
    Sep 22, 2020 at 16:14

One method that works around this for now is to write a shim .exe whose role is to call system() on tasm.exe with the /m2 parameter added at the start of the list and use this as the assembler with -Eshim.exe. A batch file doing the same doesn't seem to work.


Or is there some other method I should use to invoke my calculated pointer?

Yes. Specify that it is a 32-bit (far) pointer:

asm call DWORD PTR [xms_driver_interface]

You must log in to answer this question.

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