4

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?

8

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:

(*xms_driver_interface)();

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;
geninterrupt(0x2F);
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.

| improve this answer | |
  • Ah that's true, sorry for being blunt. That was what I tried first, but my programs crashed, so I tried the inline asm method. I think I was constructing the value of xms_driver_interface incorrectly in the question - using a union of a seg:off pair and a function pointer is giving me better results. I'd still like to know how to use inline asm correctly, though. – knol Sep 22 at 5:51
  • The added info about being explicit about symbol size helps a lot, and it does result in single pass tasm. – knol Sep 22 at 7:22
  • 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 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 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). – Stephen Kitt Sep 22 at 9:44
3

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.

| improve this answer | |
1

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]
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.