Copyright: ® Phar Lap Software, Inc.


Help Contents
Contents

Introduction

Welcome

TNT DOS-Extender Lite vs. TNT DOS-Extender SDK

TNT DOS-Extender Order Form

Copyrights

A Quick Tour of TNT DOS-Extender Lite

Overview

A Simple Example

Big Malloc

Protected-Mode C++

Protected-Mode MS-DOS and the Win32 API

BIOS Calls in Protected-Mode

Compatibility with Industry Standards

What's So Protected About Protected Mode?

Debugging with CV386 Under MS-DOS (With the TNT DOS-Extender Software Development Kit)

The Nuts and Bolts of CV386

What About Direct Screen Writes?

32-bit vs. 16-bit Performance

Summary

Building Programs for TNT DOS-Extender Lite

Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition

Using the Visual Workbench for Windows NT to Build TNT DOS-Extender Lite Programs

Using the MARKPHAR Utility

Running Your TNT DOS-Extender Lite Application

Running Under MS-DOS

Interoperability with Other System Software

The TNT Application Program Interface

Error Messages

Example Programs

Overview

FULLSCR -- Full-screen I/O

GRAPHICS -- Calling the Microsoft 16-bit Graphics Library

INTBENCH -- Several Popular Integer Benchmarks

MOUSE -- Calling the Real-Mode Mouse Driver from Protected Mode

References

Bibliography


Welcome

Welcome to the world of DOS beyond 640K!  Now, with your FREE TNT DOS-Extender Lite, it's easy to build multi-megabyte programs that run under both MS-DOS and Windows NT!  TNT DOS-Extender Lite is a special trial-sized version of Phar Lap Software's next-generation TNT DOS-Extender Software Development Kit (SDK).  It lets you use the Microsoft Visual C++ 32-Bit Edition compiler to produce 32-bit protected-mode programs that run under DOS, where they can break the 640K DOS memory barrier and access up to 2 MB of memory.  The same program can also be run as a native character-based application under Windows NT.

TNT DOS-Extender Lite includes all the tools and examples you need to build and run 32-bit Extended-DOS programs with Microsoft Visual C++, 32-Bit Edition (see Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition).  However, you may want to purchase Phar Lap's TNT DOS-Extender SDK if you would like the following features:  debugger support, access to all available memory (up to 4 GB), Phar Lap's technical support and extensive documentation, and the ability to deliver your Extended-DOS application to customers with Phar Lap's TNT DOS-Extender Run-Time Kit (RTK).  The chart TNT DOS-Extender Lite vs. TNT DOS-Extender SDK outlines the differences between TNT DOS-Extender Lite and TNT DOS-Extender SDK.  To order your SDK, just fill out the TNT DOS-Extender Order Form or contact Phar Lap.

TNT DOS-Extender SDK is available from:

Phar Lap Software, Inc.

60 Aberdeen Avenue

Cambridge, MA  02138

Phone 617-661-1510

Fax 617-876-2972

FREE PHAR LAP T-SHIRT

WITH EVERY ORDER!


TNT DOS-Extender Lite vs. TNT DOS-Extender SDK

TNT DOS-Extender Lite	TNT DOS-Extender SDK
No debugging support	Full support for 32-bit CodeView
Programs can use up to 2 MB memory	Programs can use up to 4 GB memory
50 pages of documentation on disk	1300+ pages of printed documentation
Technical support for installation	Technical support for development
Cannot deliver Extended-DOS programs to customers	Add-on run-time kit available for delivering Extended-DOS programs
Several example programs	Extensive example programs
No loading of user DLLs	Supports loading user DLLs
Limit of 1 user process and 1 user thread	Supports unlimited user processes and user threads
Supports only those functions in the C run-time library	Supports more APIs, including mixing real- and protected-mode code


TNT DOS-Extender Order Form

(Select Print Topic from the File Menu)

YES!  I would like to order ______ TNT DOS-Extender SDK x $495 each =  _______

       Massachusetts customers add 5% sales tax: _______

       Shipping:  $12 U.S. or Canada, $80 overseas: _______

TOTAL = _______

Shipping Address:  (Please provide a street address.  No P.O. boxes.)

Name:__________________________  Company:____________________________

Address: _____________________________________________________________

City:__________________________ State/Country:______________ Zip:_________

Phone:_____________________________ Fax:______________________________

Method of Payment: (Check one)

     ____ Check/money order (US$, US bank)

     ____ P.O.#(Approval required):_________________________________________

     ____ MasterCard     ____ Visa     ____ American Express

     Credit Card #:__________________________________ Exp. Date: ___________

     Cardholder's name:___________________________________________________

#TNT42792

FREE PHAR LAP T-SHIRT WITH EVERY ORDER!

Return to:  Phar Lap Software, Inc.

60 Aberdeen Avenue

Cambridge, MA  02138

Phone 617-661-1510

Fax 617-876-2972


Copyrights

Copyright (c) 1993 Phar Lap Software, Inc.

First Edition June 1993

All rights reserved.  Printed in the United States of America.  No part of this publication may be reproduced or transmitted in any form or by any means, without prior written permission of Phar Lap Software, Inc.  Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at 252.227-7013.

Microsoft C/C++ support library, Portions Copyright (c) Microsoft Corporation.

TNT DOS-Extender(TM), 286|DOS-Extender(TM), and 386|DOS-Extender(TM) are trademarks, and Phar Lap(R) is a registered trademark of Phar Lap Software, Inc.

386MAX(TM) is a trademark of Qualitas, Inc.

AutoCAD(R) is a registered trademark of Autodesk, Inc.

COMPAQ(R) is a registered trademark of Compaq Computer Corporation.

QEMM(TM) is a trademark of  Quarterdeck Office Systems.

IBM(R), AT(R), PS/2(R), and OS/2(R) are registered trademarks of International Business Machines Corporation.

Intel(R) is a registered trademark of Intel Corporation.

Lotus(R) is a registered trademark of Lotus Development Corporation.

Visual C++(TM), Windows(TM) and Windows NT(TM) are trademarks, and Microsoft(R), MS(R), MS-DOS(R), and CodeView(R) are registered trademarks of Microsoft Corporation.


A Quick Tour of TNT DOS-Extender Lite - Overview
Overview

TNT DOS-Extender Lite allows you to use the Microsoft Visual C++ for Windows and Windows NT, 32-Bit Edition compiler to build and run 32-bit Windows NT character-based applications that run as both extended MS-DOS applications and native Windows NT programs.

TNT DOS-Extender Lite applications run in 32-bit protected mode, under MS-DOS, on IBM PC and PS/2 compatibles with an 80386 or higher processor.  You can use your new Visual C++, 32-Bit Edition compiler to open up the world of 32-bit protected-mode MS-DOS (see Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition).  TNT DOS-Extender Lite programs can access up to 2 MB of memory and will run significantly faster than their 16-bit equivalents, due to the speed benefits of  32-bit vs. 16-bit Performance.

TNT DOS-Extender Lite provides an environment for developing native 32-bit character-based applications that run under both Windows NT and DOS.  The TNT DOS-Extender SDK implements a large subset of the Win32 Application Programming Interface (API), allowing developers to create versatile 32-bit applications that run in the familiar DOS environment (see Protected-Mode MS-DOS and the Win32 API).  The resulting program may be run with TNT DOS-Extender as a 32-bit DOS program or as a native Windows NT program.

Advanced features of the TNT DOS-Extender SDK (not available in TNT DOS-Extender Lite) include support for dynamic link libraries, threads, and the TNT Win32 subset (similar to the Win32s subset with the addition of the character-mode and threads functions, but without the GUI API).  Additionally, one memory-resident copy of the TNT DOS-Extender can be shared by multiple extended DOS applications, freeing more RAM than ever for use by your applications.


A Simple Example

Let's look at an example.  Many manuals begin with the "hello world" sample program, but to show the power of TNT DOS-Extender Lite we need a sample program that suffers from the 640 KB confines of MS-DOS.  Such programs are plentiful; almost any DOS program that uses large model, huge model, overlays, expanded memory (EMS), or the eXtended Memory Specification (XMS), is a good example.  Any such program is also a likely candidate for using TNT DOS-Extender Lite!

But short of including 50,000 lines of code in our manual, how can we provide a quick look at breaking the 640 KB barrier?

We can simulate some "programming in the large" issues by using a small C program that requires a large amount of memory.  The program shown below, BIG.C, attempts to allocate 1 MB of memory, and to use it as a 512 x 512 two-dimensional array of longs:

/* BIG.C */

#include <stdlib.h>

#include <stdio.h>

#include <malloc.h>

#define NROWS    512L

#define NCOLS    512L

#define ARSIZE (NROWS * NCOLS * sizeof(long))

main()

{

    int i, j;

    long (*array)[NCOLS];

    array =  malloc(ARSIZE);

    if( ! array )

    {

       printf("Can't allocate %lu-byte array, giving up!\n", ARSIZE);

       return 1;

    }

    printf("Using %lu-byte array\n", ARSIZE);

    for (i=0; i<NROWS; i++)

    {

        for (j=0; j<NCOLS; j++)

            array[i][j] = (long) i * j; /* touch every element */

        printf("%d\r", i);              /* display odometer */

    }

    printf("done\n");

    return 0;

}

This program cannot even be correctly compiled for real-mode DOS.  The argument to real-mode malloc() is of type size_t, a 16-bit quantity, and BIG.C passes a value of 1,048,576, too large for 16 bits.  Even if we were to change the call to malloc() to a series of calls, each allocating only 64K (the largest legal 16-bit quantity), the program would fail.  Real-mode MS-DOS simply cannot address, much less allocate, more than 1 MB of memory.  Even if the computer has 16 MB, MS-DOS can only directly address the first megabyte.  The 640KB barrier prevents the allocation of even 1 MB of memory on machines with significantly more memory.

But to run BIG.EXE you don't have to switch to a different operating system.  TNT DOS-Extender Lite removes the 1 MB DOS barrier, while continuing to run under DOS.  You get to have your cake and eat it too.

To produce a TNT DOS-Extender Lite version of the BIG program, simply compile and link your program using the Visual C++, 32-Bit Edition compiler and linker (see Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition).  After you've built your Windows NT executable program, run the Phar Lap REBIND utility to prepare the program for TNT DOS-Extender Lite.  That's it!

C:\>cl big.c

C:\>rebind big

C:\>big

Using 1048576-byte array

done

We just allocated and accessed 1 MB under MS-DOS, in a program whose name we typed on the DOS command line — just like any other regular DOS program.  The source code for the program, as we've seen, uses absolutely standard PC constructs:  no messing with special interfaces like EMS or XMS, and no overlays.

It's as if MS-DOS had been turned into a protected-mode operating system, or at least into an operating system that can run protected-mode programs!  This in fact is exactly what TNT DOS-Extender Lite does.  Behind the scenes, when BIG is run from the DOS command line, the program reinvokes itself under TNT DOS-Extender Lite, which resides in a program named TNTLITE.EXE.  (Incidentally, TNTLITE.EXE must be somewhere on the path.)  Running BIG is equivalent to running TNTLITE BIG. TNTLITE puts the machine into protected mode, runs BIG, then puts the machine back into real mode.

Clearly, BIG is not a typical real-mode DOS program.  It does, however, simply illustrate the problem presented by the 640 KB barrier in real-mode DOS and the ease with which this barrier is removed by TNT DOS-Extender Lite.  You should note that the TNT DOS-Extender Lite version of BIG also runs as-is under Windows NT.


Big Malloc

The enormous allocation used in BIG.C (see A Simple Example) provides a simple first example of TNT DOS-Extender Lite.  In the same way that TNTLITE can allocate large amounts of data, it can also load programs with large amounts of code, so the huge array is a good simulation of a large program.  Most C programs do not allocate huge arrays, but many DOS programs do contain enormous amounts of code.  Such programs often resort to performance-crippling overlays; BIG shows that, on an 80386 or higher machine with extended memory, this workaround is not necessary.

Rather than initially allocating an enormous array, it is more typical for large PC applications to dynamically allocate memory via the C malloc() function, via operator new in C++, or directly via the MS-DOS Allocate Memory Block function (INT 21h AH=48h).

Our next example program, MEMTEST, does nothing more than run malloc() in a loop and touch every byte of the allocated memory.  When malloc() eventually returns NULL, indicating that memory is exhausted, MEMTEST breaks out of the loop.  While the program clearly does not do anything useful with the memory it allocates, its insatiable appetite for memory is a very good simulation of a large application:

/* MEMTEST.C */

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

main()

{

    char *p;

    unsigned long allocs;

    for (allocs = 0; ; allocs++)

       if ((p = malloc(1024)) != 0)    /* in 1k blocks */

       {

           memset(p, 0, 1024); /* touch every byte */

           *p = 'x';           /* do something, anything with */

           p[1023] = 'y';      /* the allocated memory      */

           if (allocs && (allocs % 1024) == 0)   /* odometer */

               printf("Allocated %u megabytes\r", allocs >> 10);

       }

       else

           break;

       printf("Allocated %lu bytes\n", allocs << 10);

       return 0;

}

Using a 16-bit compiler such as Visual C++, this program can be compiled for real-mode MS-DOS:

C:\>cl -AL memtest.c

This command produces a large-model version of MEMTEST.EXE, allowing maximum memory allocation under real-mode MS-DOS.  On a DELL 486 with 8 MB of memory, MEMTEST should allocate around 8 MB of memory.  (If you don't think this is what MEMTEST should do, you've been programming in real mode for too long!)  But of course, here is what real-mode large-model MEMTEST does instead:

C:\>memtest

Allocated 442368 bytes

Only 432 KB available on an 8 MB machine?!  By making better use of the DOS 5.0 LOADHIGH, DEVICEHIGH, and DOS=HIGH,UMB statements, or by using superb expanded memory managers such as Quarterdeck QEMM or Qualitas 386MAX, we could probably have created a better configuration, in which MEMTEST might get as much as 610 KB or so.  But no matter how much we huffed and puffed and fooled with CONFIG.SYS, real-mode MEMTEST would never allocate more than 1 MB of memory.

Furthermore, even if you do maximize the available DOS memory on one machine, there is no guarantee that all (or even most) of a program's users will have such an "ideal" configuration.  By developing for protected-mode DOS, you can bypass a lot of these messy user-configuration issues.

Now, to produce a version of MEMTEST that behaves more sensibly, the only thing you need to do differently is to run with TNT DOS-Extender Lite:

C:\>cl memtest.c

C:\>rebind memtest

C:\>memtest

Allocated 2097152 bytes

We allocated 2 MB of memory under MS-DOS!  (If we were using the TNT DOS-Extender SDK, we could have allocated the full 8 MB.)  This is dramatically different behavior from the first version of MEMTEST we produced, yet we're using the same source code.  Simply rebuilding for TNT DOS-Extender Lite, without source code changes, gives us a program that runs under DOS yet breaks the 640K barrier, without EMS, overlays, or other workarounds.  Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition provides a version of malloc() that transparently uses extended memory, plus functions such as memset() and pointer dereferences such as *p and p[1023], which transparently access extended memory.


Protected-Mode C++

TNT DOS-Extender Lite is not limited to programs written in C; C++ programs can just as easily run under protected-mode DOS.  For example, the C++ operator "new" works in protected-mode DOS in exactly the same way as the C malloc() function:  instead of halting somewhere around 640K, operator new can be used to allocate up to 2 MB of memory under TNT DOS-Extender Lite.  (Under TNT DOS-Extender SDK, a program can allocate up to 4 GB.)

Other C++ features, such as the iostream input and output operators and static constructors and destructors, work just the way one would expect from having used C++ in real-mode DOS.

The next sample program, MEMTEST2.CPP, uses a number of C++ features.  The C++ set_new_handler() function is particularly useful; it lets MEMTEST2 install a handler that will be called automatically when operator new fails.  This considerably simplifies the program's main loop:

/* MEMTEST2.CPP */

#include <stdlib.h>

#include <iostream.h>

#include <new.h>

#include <string.h>

class msg {

public:

    msg()   { cout << "hello from " << __FILE__ << "\n" ; }

    ~msg()  { cout << "bye\n" ; }

    } ;

static msg banner;  // test C++ static constructors, destructors

static unsigned long bytes = 0;

static unsigned long allocs = 0;

static unsigned blk_size = 10240;

int new_fail(size_t )

{

    if (blk_size)

        blk_size >>= 1;  // try to allocate a smaller block

    else

    {   // memory exhausted

        cout << "Allocated " << bytes << " bytes\n" ;

        exit(1);

    }

    return 1;

}

void

main(void)

{

    char *p;

    _set_new_handler(new_fail);   // called when new fails

    for (;;)

    {

       p = new char[blk_size];   // allocate memory

       memset(p, 0, blk_size);   // touch every byte

       *p = 'x';                 // do something, anything with

       p[blk_size-1] = 'y';      //   the allocated memory

       bytes += blk_size;

       allocs++;

       if ((allocs % 25) == 0)   // odometer

            cout << "Allocated " << bytes << " bytes\r";

    }

}

We won't even bother showing the real-mode version this time, since by now it's pretty obvious how limited real mode is.  To build this C++ program for TNT DOS-Extender Lite, all you need to do is compile with the Visual C++, 32-Bit Edition compiler and REBIND:

C:\>cl memtest2.cpp

C:\>rebind memtest2

C:\>memtest2

hello from memtest2.cpp

Allocated 2097152 bytes

bye

Because TNT DOS-Extender Lite is compatible with the DPMI specification (as well as with every other major memory-management specification, including VCPI, XMS, VDS, and INT 15h), this protected-mode C++ program can allocate up to 2 MB of memory under all modes of Windows 3.x.


Protected-Mode MS-DOS and the Win32 API

TNT DOS-Extender Lite can load programs with huge arrays or enormous amounts of code. Protected-mode DOS versions of C functions such as malloc(), and of C++ operators such as new, can dynamically allocate large amounts of memory.  How does all this work?

While generally used with C or C++ programs, TNT DOS-Extender Lite is not a C or C++ library.  The real magic occurs at a lower level.

In essence, TNT DOS-Extender Lite provides 32-bit protected-mode MS-DOS (INT 21h) functions as well as a subset of the Win32 API.  Since your program runs in protected mode, any software interrupts it generates occur in protected mode too, and are caught by TNT DOS-Extender Lite.  These include DOS calls made by the C run-time library when you call a function such as printf(), as well as explicit INT calls made by your program.

In most cases, TNT DOS-Extender Lite passes the call down to MS-DOS, which is still running in real mode.  DOS file I/O functions are handled this way, for example:  the TNT DOS-Extender Lite handler for these functions reissues (or "reflects") the call in real mode.

In contrast, the Win32 functions are actually implemented by TNT DOS-Extender Lite.  This implementation may use DOS functions or interrupts if appropriate.

Because TNT DOS-Extender Lite acts as a protected-mode "wrapper" around real-mode MS-DOS, you have a completely compatible environment.  TNT DOS-Extender Lite isn't replacing DOS, so it will work with whatever version of DOS you or your customers already have.

Of course, real-mode MS-DOS does not know how to do things like run protected-mode programs, allocate extended memory, or install protected-mode interrupt handlers.  Requests such as these are handled by TNT DOS-Extender Lite entirely in protected mode.

Memory allocation is a good example.  Real-mode programs might use the Allocate Memory Block function (INT 21h AH=48).  This technique is not suitable for TNT DOS-Extender Lite because it uses a far pointer to address the memory, and Visual C++, 32-Bit Edition does not support far pointers.  However, TNT DOS-Extender supports a large subset of the Win32 API, providing these 32-bit protected-mode services to your DOS program.  Rather than use C or C++ constructs to allocate memory, the next sample program, VIRMEM.C, uses the Win32 VirtualAlloc() function first to reserve 2 MB of memory (blocked into 32 pages of 64 KB each), and then to commit each page.

/*  VIRMEM.C */

#include <windows.h>

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#define PAGESIZE  64*1024  // 64KB pages

#define NUM_PAGES 32       // this gives us 2 MB total

main()

{

char *base, *page_ptr;

char j;

long num_bytes = 0;

   base = (char *) VirtualAlloc(NULL, PAGESIZE*NUM_PAGES,

                          MEM_RESERVE, PAGE_NOACCESS);

   if (base)

      {

         // reservation succeeded

         printf("Base address of memory block: %p\n",base);

         for (j=0; j< NUM_PAGES; j++)

         {

            //  now commit each page and use it

            page_ptr = (char *) VirtualAlloc(base, PAGESIZE,

                                       MEM_COMMIT, PAGE_READWRITE);

            if (page_ptr)  // commit succeeded

            {

               base += PAGESIZE;

               page_ptr[1] = 'a';

               printf("Allocating page %u\r ", j);  //odometer

               num_bytes += PAGESIZE;

            }

            else

            {

               printf("\nCould not allocate page number %u \n",j);

               printf("total bytes allocated = %xH\n",num_bytes);

               return(1);

            }

         }

      }

   else   //reservation failed

   {

      printf("Initial call to VirtualAlloc failed\n");

      return(1);

   }

   printf("\nTotal bytes allocated = %xH\n",num_bytes);

   return(0);

}

Since VIRMEM is calling a Win32 function, we have to define this API to our program:

#include <windows.h>

When including WINDOWS.H, you also have to add the following compiler directives to the command line:

/D_X86_=1 /DWIN32

The /D is a preprocessor directive that defines symbols used to control conditional compilation (#if or #ifdef directives).  The symbols "_X86_" and "WIN32" define the target as a Win32 application running on an Intel or compatible 386 or higher CPU.  Preprocessor directives help when porting code to other Windows NT-supported processor platforms.

Including these options, the program is built by the commands:

C:\>cl /D_X86_ /DWIN32=1 virmem.c

C:\>rebind virmem

The resulting program easily allocates 2 MB of memory under DOS:

C:\>virmem

Base address of memory block: 00060000

Allocating page 31

Total bytes allocated = 200000H

When protected-mode VIRMEM allocates 2 MB, where does this memory come from?  In general, this doesn't matter to the program, because it has asked VirtualAlloc() for memory, and has received back a pointer to that memory.  Thus, we have immediately usable memory; it doesn't really matter where it "comes from," nor for that matter what value VirtualAlloc() returns.  All we need to know is that we can use it.


BIOS Calls in Protected-Mode

To create a useful environment for PC software, it is not sufficient merely to provide INT 21h services in protected mode.  The ROM BIOS calls (INT 10h, INT 16h, and so on) need to be available in protected mode too.

Protected-mode DOS programs can call BIOS functions from separate .ASM modules assembled with MASM, or by using inline assembler.  (The TNT DOS-Extender SDK also provides C run-time library functions such as _bios_serialcom(), _bios_printer(), and _bios_keybrd() in protected mode.)

For example, if our sample program did direct screen writes (see What About Direct Screen Writes?), it might include initialization code such as the following, which uses INT 10h Function 0Fh (Get Video Mode):

#define GET_VIDEO_MODE      0x0F

int video_mode(void)

{

    _asm mov ah, GET_VIDEO_MODE

    _asm int 10h

    _asm and eax, 0FFh

}

The video_mode() function runs just fine in protected mode, because TNT DOS-Extender Lite supports INT 10h (BIOS Video) in protected mode.

The following list shows all interrupts and functions that TNT DOS-Extender Lite supports in protected mode — the vast majority of DOS (INT 21h) and BIOS (INT 10h, INT 16h, etc.) calls:

INT 05h (Print Screen)

INT 10h (BIOS Video)

Functions 00h - 10h

Functions 12h - 13h

Functions 1Ah - 1Ch

Functions FEh - FFh

INT 11h (BIOS Equipment Check)

INT 12h (Get Conventional Memory Size)

INT 13h (Floppy Disk and Fixed Disk I/O)

INT 14h (Communications Services)

Functions 00h - 03h

INT 15h (System Services)

Function  84h

Function  86h

Function  88h

Functions C0h - C1h

INT 16h (Keyboard Services)

Functions 00h - 05h

Functions 10h - 12h

INT 17h (Printer Services)

Functions 00h - 02h

INT 19h (Reboot)

INT 1Ah (Time and Date Services)

Functions 00h - 07h

INT 1Bh (Ctrl-Break)

INT 1Ch (Tick Counter)

INT 1Dh (Pointer to Video Parameter Table)

INT 1Eh (Pointer to Floppy Disk Parameter Table)

INT 1Fh (Pointer to Graphics Characters Dot Table)

INT 21h (MS-DOS Calls)

Functions 00h - 17h

Functions 19h - 1Ch

Functions 21h - 36h

Functions 38h - 52h

Function  54h

Functions 56h - 5Ch

Function  5D06h

Function  5D0Bh

Functions 5E00h - 5E03h

Functions 5F02h - 5F04h

Function  60h

Function  62h

Functions 65h - 68h

Function 6C00h

INT 22h (Address of Terminate Routine)

INT 40h (Floppy Disk I/O)

INT 46h (Pointer to Fixed Disk Drive 2 Parameter Table)

INT 4Ah (Real-Time Clock Alarm Service)


Compatibility with Industry Standards

TNT DOS-Extender Lite (that is, TNTLITE.EXE) manages the interface between your protected-mode program and real-mode MS-DOS.  At the DOS command line, when you type the name of a protected-mode .EXE file, TNTLITE puts the machine into protected mode and launches your program.  Whenever your program makes DOS or BIOS calls from protected mode, TNT DOS-Extender Lite handles them and/or passes them down to MS-DOS or the ROM BIOS.  When your program exits, TNT DOS-Extender Lite puts the computer back into real mode.

But things aren't always so simple in the PC world of the 1990s.  The assumption that a DOS extender can simply switch the machine from real mode to protected mode does not take into account the fact that many machines won't be in real mode.  Most users of 80386 and 80486 machines are employing memory managers such as Microsoft EMM386, Qualitas 386MAX and Quarterdeck QEMM, or multitasking window systems such as Microsoft Windows 3.x (Enhanced mode).  When such programs are running, the computer is not in real mode; it's running in a 1 MB protected mode that Intel calls virtual 8086 mode.  Furthermore, users may be running programs such as VDISK or HIMEM or SMARTDrive, which use extended memory.  TNTLITE must be able to run your protected-mode program under DOS, yet be highly compatible with any other memory-management software a user might be running.

One of the key questions about any protected-mode MS-DOS solution is how compatible it is with all this different software.  Is it compatible with EMM386, 386MAX, and QEMM?  Does it run in a window inside Microsoft Windows 3.x Enhanced mode?  What if the machine is already running an expanded memory (EMS) or extended memory (XMS) manager?  What if there are VDISKs or a SMARTDrive?  What if your protected-mode program has been spawned from within a 386|DOS-Extender program such as FoxPro, Mathematica, or AutoCAD/386?

TNT DOS-Extender Lite handles all these situations gracefully.  It is compatible with all the PC industry standards for protected mode, expanded memory (EMS), and extended memory (XMS).  It supports the Virtual Control Program Interface (VCPI) and DOS Protected Mode Interface (DPMI).  VCPI, a specification developed jointly by Quarterdeck Office Systems and Phar Lap Software, allows EMS emulators and DOS extenders to coexist; it is an extension to EMS 4.0.  DPMI is a specification whose primary goal is compatibility between DOS extenders and DOS multitasking environments; DPMI was jointly developed by a committee including Microsoft, Intel, IBM, Phar Lap, Quarterdeck, Borland, Lotus, Rational Systems, and other companies.  For more details on VCPI and DPMI, plus the old INT 15h and XMS standards, we recommend the book Extending DOS, Second Edition, edited by Ray Duncan (Reading MA: Addison-Wesley, 1991, 538 pp., ISBN 0-201-56798-9).

To sum it all up, programs developed with TNT DOS-Extender Lite can run in a wide variety of DOS environments.  All this compatibility requires no work from you.  There is no configuration or "tune" program to run.  TNT DOS-Extender Lite automatically configures itself each time it runs. If run under Windows 3.x Enhanced mode, for example, the program will automatically get virtual memory with no additional effort.  Or if you switch from HIMEM.SYS to QEMM386.SYS, you don't have to reconfigure TNTDOS-Extender Lite in any way.


What's So Protected About Protected Mode?

If you're like most C programmers, you've had at least some experience with a wild pointer wreaking havoc on your program.  A pointer can get a bad value in a variety of ways: failure to check the return value from malloc(), faulty pointer arithmetic (particularly on machines with segmented architecture, such as those in the 80x86 family), or an inaccurate assignment.  To keep things simple, let's look at BAD, an obviously bad program with a blatantly wrong pointer assignment statement:

/*

BAD.C

*/

char *setptr()

{

   return((char*)-1);

}

void main ()

{

   char *ptr;

   ptr = setptr();

   *ptr = 'a';

   printf("%c\n",*ptr);

}

If this program is compiled and run in real mode, the computer will hang trying to reference the -1 pointer.  Most likely, you will have to reboot your computer in order to make it usable again.

What happens in protected mode when we try to dereference (peek or poke) the bad pointer?  Instead of attempting to do something with the -1 pointer, the program halts with a message like this:

Fatal error TNTLITE.1020: unhandled exception occurred:General protection fault.

ProcessId=9740 ThreadId=9741

User Registers:

EAX=FFFFFFFFh EBX=00000000h ECX=00000000h EDX=0005E000h

ESI=00000000h EDI=00000000h EBP=00057FB4h ErrorCode = 80000000h

DS=00BFh ES=00BFh FS=00EFh GS=00BFh FLG=00010202h

CS:EIP=00B7h:00040029h SS:ESP=00BFh:00057FA4h

The message "General protection fault" is an indication that the program attempted an invalid memory reference.  A General protection fault is one of a group of unhandled exceptions trapped by TNT DOS-Extender Lite.  Page fault and General protection fault are the most common exceptions in protected mode.

A General protection fault is an indication that the program has in some way violated the rules of protected mode.  These rules prevent writing into code segments, executing data segments, reading or writing past the end of a segment, or using a segment that doesn't belong to you.

The fault shows either a bug in your program, or an area that needs to be converted so that it will work in protected mode.  In this case, the CPU signals a fault when your program tries to violate protection.  This makes protected mode a superb environment for software development:  the hardware will help you find bugs and trouble spots.

In the case of the buggy program BAD, the "General protection fault" message alerts us to a bug that the CPU found for us:  the program dereferences pointers without checking their validity.  If you examine the register dump produced by TNT DOS-Extender Lite, you will note that the EAX register is set to FFFFFFFFh, suggesting that a -1 is being used incorrectly.  This pointer checking is not inserted by the compiler:  the Intel processor takes care of it in hardware, making it just one example of the many checks that the CPU does in protected mode, with no extra work on our part.


Debugging with CV386 Under MS-DOS (With the TNT DOS-Extender Software Development Kit)

As explained earlier, TNT DOS-Extender Lite does not support debuggers.  The TNT DOS-Extender SDK, however, supports the powerful debugging features of the protected-mode 32-bit CodeView debugger, CV386.  We would like to take this opportunity to illustrate the debugging support available with the TNT DOS-Extender SDK.

Let's look at the buggy BAD program from What's So Protected About Protected Mode?.  A protected-mode symbolic debugger like 32-bit CodeView lets us pinpoint the exact location of the bug in the BAD program (if we didn't already know it).

We run CV386 under MS-DOS, right from the DOS prompt, without Windows.

To make symbolic information available for CV386 under DOS, we use the Visual C++, 32-Bit Edition /Zi switch:

C:\>cl /Zi bad.c

C:\>rebind bad

The Nuts and Bolts of CV386 provides specifics for debugging with CV386.


The Nuts and Bolts of CV386

To debug a protected-mode 32-bit program under DOS, run CV386, together with your program:

C:\>cv386 bad

If you are familiar with CodeView, you can do source-level debugging of protected-mode programs without having to learn a new debugger.  If you are not familiar with CodeView, then using TNT DOS-Extender is an excellent way to get started.  Running CodeView in protected mode helps you to catch bugs as they happen, by signaling General protection ("GP") faults.  This is in contrast to what occurs in real mode, where programs are allowed to dereference pointers willy-nilly, often corrupting data structures in obscure, hard-to-find ways.  However you learn CodeView, you will want to read Microsoft's documentation to completely understand each command.

If you've used real-mode CodeView, but have not done much protected-mode programming, there's only one thing you should be aware of:  protected-mode selectors don't look quite the same as the real-mode paragraph addresses you're used to.  Typical selector values are small numbers such as 000Fh for the code segment and 0017h for the data segment.

Essentially all the other features in CV386 work exactly as you would expect.  You can switch between displaying source code and object code with F3, you can use F2 to display the current register set, and you can use F9 and F5 to set a breakpoint and run your program, respectively.  If the HELPFILES environment variable is set correctly, F1 invokes CodeView's built-in help system.  Of course you may invoke all functions either by pulling down menus or by typing in the command window, just like real-mode CodeView.

When debugging BAD (from What's So Protected About Protected Mode?), if you type "g", the bad pointer reference will cause a General Protection fault.  CV386 will run into the offending code, and place the cursor on the

*ptr = 'a';

line of code and display this message:

Exception 0x0000000D: General Protection Fault

Sure enough, forgetting to check the return value from setptr() causes an "exception 13" (also known as an INT 0D, a General protection violation, or a GP fault).  The register window would show that the EAX register holds a value of -1, indicating that we're trying to do something with a bad pointer.

Where in our code has this exception occurred?  CV386 highlights the line of code which caused the fault, pinpointing the specific bad assignment.  In our little BAD program, we know that the bad assignment was made in procedure setptr().  However, in a typical application, the assignment could have been made anywhere.  We can set a CodeView Watch window to follow the assignments to ptr.  We do this with the "W? ptr" command.  We then restart the program with the "l" command, and position CV386 at the beginning of main with the "g main" command.  We can then step through main with the "p" command, watching the value of ptr in the Watch window.  We see that it acquires the value of -1 after the statement "ptr = setptr();" is executed.

Can you imagine debugging this kind of code in a large application without CodeView?  We hope you will see from this example how much easier protected-mode programming can be with the features available in the TNT DOS-Extender SDK.


What About Direct Screen Writes?

With the exception of programs such as compilers and linkers, applications for the PC tend to use direct screen writes or other low-level code.  How well do such programs run under TNT DOS-Extender Lite?

DOS (INT 21h) and BIOS calls are supported in protected mode.  Thus, low-level PC code runs as is under TNT DOS-Extender Lite.

However, the PC programming interface doesn't consist only of DOS and BIOS calls.  The ability to peek and poke absolute memory locations is also part of the PC programming interface, and this ability normally assumes that the program is running in real mode.

Consider direct screen writes, for example.  These assume that absolute memory locations such as B8000h can be addressed through pointers such as B800:0000.  When a program is manipulating an address such as B800:0000, it's really interested in absolute memory location B8000h.  Similarly, when it manipulates an address such as 0040:006C, it's really interested in absolute memory location 46Ch.  In protected mode the equivalence of B8000h and B800:0000 no longer holds.  It is precisely because protected mode throws out this equivalence that it can provide more than one megabyte of memory.

Besides the tiny video_mode() function we saw in BIOS Calls in Protected-Mode, a program that does direct screen writes probably would also include code such as the following, which puts the paragraph address of the text mode video display memory (0xB000 or 0xB800) into a global variable.  This real-mode code also uses the _MK_FP() macro from DOS.H to create a far pointer to B000:0000 or to B800:0000:

unsigned short get_vid_mem(void)

{

    int vmode = video_mode();

    unsigned short seg;

    if (vmode == 7) /* monochrome */

       seg = 0xB000;

    else if ((vmode == 2) || (vmode == 3))

        seg = 0xB800;

    else

       return 0;

    return seg;

}

static BYTE far *vid_mem;   /* use far pointer */

video_init(void)

{

    vid_mem = MK_FP(get_vid_mem(), 0);

}

#define SCR(y,x)    (((y) * 160) + ((x) << 1))

void wrt_str(int y, int x, ATTRIB attr, unsigned char *p)

{

    BYTE far *v = vid_mem + SCR(y, x);

    while (*p)

    {

       *v++ = *p++;

       *v++ = attr;

    }

}

There are a couple of things that have to be addressed before this code can be run under TNT DOS-Extender Lite: the use of far pointers (MK_FP), and the direct use of the paragraph address of the video display memory.

It's extremely simple to get this code working in protected mode.  TNT DOS-Extender Lite provides functions for reading and writing directly to real-mode memory locations.  We can use the function WriteRealMem() to directly address video memory.  Rather than writing each character directly to video memory, the function wrt_str() is modified to accumulate the data in an internal buffer and then use WriteRealMem() to write the whole buffer to the screen.  We also modify video_init() to create the actual real-mode address of the video memory.

#include <stdlib.h>

#include <stdio.h>

#include <stdarg.h>

#include <dos.h>

typedef unsigned char BYTE;

typedef unsigned short USHORT;

typedef unsigned long ULONG;

typedef unsigned long REALPTR;

#define VIDEO               0x10

#define SCROLL_UP           0x06

#define GET_VIDEO_MODE      0x0F

static REALPTR vid_mem = {0};

void __stdcall WriteRealMem(REALPTR, void *, unsigned);

void __stdcall ReadRealMem(void *, REALPTR, unsigned);

#define SCR(y,x)            (((y) * 160) + ((x) << 1))

unsigned get_vid_mem(void)

{

    int vmode = video_mode();

    unsigned vid_seg;

    if (vmode == 7)

        vid_seg = 0xB000;

    else if ((vmode == 2) || (vmode == 3))

        vid_seg = 0xB800;

    else

        return 0;

    return vid_seg;

}

int video_init(void)

{

    vid_mem = get_vid_mem() << 16;

}

void wrt_str(int y, int x, ATTRIB attr, unsigned char *p)

{

    REALPTR v = vid_mem + SCR(y, x);

    char lbuff[80 * 2], *q;

    int ok;

    q = lbuff;

    ok = 0;

    while (ok < 80 && *p)

    {

        *q++ = *p++;

        *q++ = attr;

        ++ok;

    }

    WriteRealMem(v, lbuff, ok);

}

Simply by calling ReadRealMem() and WriteRealMem(), the larger program that includes this code is able to run under TNT DOS-Extender Lite.  (This larger program is included in the \EXAMPLES\FULLSCR directory.  See FULLSCR -- Full-screen I/O.)  All the actual direct screen writes run in protected mode without modification.  Most graphics code can be ported to TNT DOS-Extender Lite in the same way, simply by using the functions ReadRealMem() and WriteRealMem() to access the video memory.

That's all it takes to get direct screen writes working in protected mode.


32-bit vs. 16-bit Performance

Intel 80386 and higher processors can run either 16-bit or 32-bit programs.  One of the advantages of using the Visual C++, 32-Bit Edition compiler is that it generates 32-bit code, which is faster than the equivalent 16-bit code.   The terms 16-bit and 32-bit code refer to the size of the operands and the data paths to and from memory.  Consider, for example, the multiplication of two 32-bit integers to yield a 32-bit result.  In the 32-bit world, this can be accomplished with a single instruction.  In the 16-bit world, however, it takes four multiplications and two additions to complete the operation.

We can see this difference if we look at the program in the TNT DOS-Extender Lite EXAMPLES\INTBENCH directory (see INTBENCH -- Several Popular Integer Benchmarks).  We first compiled the program using the 16-bit Visual C++ compiler with the /AL (large memory model) and /Ogit (enable global-level common subexpression optimization, generate intrinsic functions, and minimize execution speed) options.  Running this program on a Dell 486P/50 computer with 8 MB of memory gave the following results:

16-bit Protected Mode Benchmarks

      Benchmark         Iters     Time

---------------------- -------  -------

Fibonacci                  100    10.32 secs

Sieve                      100     1.32 secs

Sort                       100     2.69 secs

Dhrystone 1.1            50000     2.20 secs  <<22727>> dhrystones

                                -------

Total execution time              16.53 secs    Performance index -- 31.24 *

*  The performance index gives the number of times faster this machine runs compared to an 80286 PC (6 Mhz)

We then compiled this same program (after changing the 16 to a 32 in the title text string) using the Visual C++, 32-Bit Edition compiler with the /Ogit option.  After REBINDing for TNT DOS-Extender Lite (see Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition), we ran the program on the same computer.  The results clearly show the difference in speed between 16-bit and 32-bit code:

32-bit Protected Mode Benchmarks

      Benchmark         Iters     Time

---------------------- -------  -------

Fibonacci                  100     7.47 secs

Sieve                      100     0.44 secs

Sort                       100     0.77 secs

Dhrystone 1.1            50000     1.15 secs  <<43478>> dhrystones

                                -------

Total execution time               9.83 secs    Performance index -- 52.54 *

*  The performance index gives the number of times faster this machine runs compared to an 80286 PC (6 Mhz)

While the actual improvement in execution time is just over 40%, the performance index increased by a whopping 68%!  The only change we made was using the Visual C++, 32-Bit Edition compiler and running the program with TNT DOS-Extender Lite.  These are the results we got running on our system; your mileage may vary.

As we stated earlier, 32-bit arithmetic can be implemented in fewer microcode instructions than 16-bit code.  In addition, 32-bit addressing can also be faster, particularly if your program uses the large memory model or contains large arrays.  The additional loading of segment registers needed in large-model 16-bit programs is not necessary in 32-bit programs (where the large model has no meaning).  If your 16-bit program needs an array larger than 64 KB, you have to use huge pointers which can be very slow; 32-bit programs do not have this limit.


Summary

Any given program probably will require only a few changes; the vast majority of your DOS code will work "as is" under TNT DOS-Extender Lite.

TNT DOS-Extender Lite turns Microsoft Visual C++, 32-Bit Edition into a toolkit for 32-bit protected-mode DOS development.  Just by REBINDing your .EXE files, your 32-bit C++ programs run on MS-DOS and break the 640K barrier.


Using TNT DOS-Extender Lite with Visual C++, 32-Bit Edition

The Visual C++ for Windows and Windows NT, 32-Bit Edition compiler can be used either from a DOS prompt or in the Windows NT environment.  However, the easiest way to build TNT DOS-Extender Lite protected-mode programs is to use the command-line compiler, CL, which has an interface very similar to the 16-bit Microsoft C/C++ compilers (CL):

C:\TEST> cl memtest.c

The real-mode stub program bound onto an .EXE file by the Microsoft linker is not suitable for running with TNT DOS-Extender Lite:

C:\>memtest

This program cannot be run in DOS mode.

Once you have linked your program, use the following command to replace Microsoft's real-mode stub with the TNT DOS-Extender stub loader:

C:\>rebind memtest

You can then run the program simply by typing the name of the .EXE file:

C:\>memtest

Allocated 2097152 bytes...  ok

Because many of the tools in the 32-bit Microsoft C compiler have the same names as their 16-bit counterparts, we recommend that you set up two batch files which switch between your 32-bit tools and your 16-bit tools.  The idea is to have a simple way to set the appropriate PATH and environment variables to run the particular compiler and linker.  Before you build a TNT DOS-Extender Lite program, run a batch file to select the proper Microsoft tools.  This file should contain commands such as the following:

@echo off

set TOOLROOTDIR=c:\msvcnt

path %TOOLROOTDIR%\bin;c:\msvc32s\pharlap\bin;%basepath%

set lib=%TOOLROOTDIR%\lib

set include=%TOOLROOTDIR%\include

TOOLROOTDIR should be the directory in which you've installed the Visual C++, 32-Bit Edition compiler.  We've assumed there is an environment variable named BASEPATH that contains all the directories that belong in the PATH, except those containing the Microsoft compiler tools and TNT DOS-Extender.  You can either set BASEPATH in your AUTOEXEC.BAT file or list all directories when setting the path for the Visual C++, 32-Bit Edition compiler.


Using the Visual Workbench for Windows NT to Build TNT DOS-Extender Lite Programs

Visual Workbench can be used to build 32-bit protected mode programs for TNT DOS-Extender Lite.  Refer to the Microsoft Visual Workbench User's Guide for the basic information on using this environment.  The only special action you need to perform is specifying GOLITE.EXE as the real-mode MS-DOS stub program.

Select Project from the Options menu.  In the Project Options dialog box, make sure the Project Type is "Console Application (.EXE)".  Note the check box labeled "Use Microsoft Foundation Classes".  This box should be checked ONLY if your program uses the MFC libraries.  This is particularly important if you did not load the MFC files (to save disk space, for example).

Select the Linker button to open a Linker Options dialog box.  Choose the Common to Both button in the Build Options list at the top of the box so that the settings are available for all project builds.  (Even though TNT DOS-Extender Lite does not support debuggers, this is a good habit to acquire, particularly if you eventually get the TNT DOS-Extender SDK.)  Select Input from the Category list.  The MS-DOS Stub box is at the bottom right of the window; type the full path for GOLITE.EXE (e.g., C:\MSVC32S\PHARLAP\BIN\GOLITE.EXE) in this box.

After you've built your program (probably by having chosen Build from the Project menu), you can run it as a Windows NT program from within Visual Workbench, or as an extended DOS program either from a Windows NT DOS box or on a DOS machine.


Using the MARKPHAR Utility

What if your program uses TNT DOS-Extender Lite features not supported by Windows NT?  Could you run the program as a TNT DOS-Extender application under Windows NT?

TNT DOS-Extender Lite features not supported by Windows NT are those that allow your program to interface with real-mode code or data.  These features include ReadRealMem(), WriteRealMem(), and the other real-mode API functions in TNTAPI.LIB, as well as the traditional DOS INT 21h and BIOS calls.  If you try to run a program that uses any of these functions (for example, FULLSCR -- Full-screen I/O, GRAPHICS -- Calling the Microsoft 16-bit Graphics Library, or MOUSE -- Calling the Real-Mode Mouse Driver from Protected Mode) under Windows NT, you will get an "Application Error."

The MARKPHAR utility is provided to solve this problem.  Programs that have been post-processed by MARKPHAR can be run as TNT DOS-Extender Lite applications directly under Windows NT (for example, using the File menu Run command from Program Manager).  The MARKPHAR command line takes one argument, the name of an executable file:

c:\>markphar filename

How does MARKPHAR make this happen?  All executable files contain a file-type signature in the header.  The default Windows NT signature is PE; MARKPHAR changes the signature to PL.  When Windows NT sees a PE signature, it ignores the real-mode stub and runs the protected-mode portion of the program.  This is fine unless the protected-mode code uses real-mode data or calls real-mode code.  However, when Windows NT sees a PL signature, it ignores the protected-mode portion of the program and runs the real-mode stub.  Because our program has been post-processed by REBIND, the real-mode stub starts up TNT DOS-Extender Lite which in turn loads the protected-mode portion of the program.  Voila — success!!


Running Under MS-DOS

A TNT DOS-Extender Lite program can be run directly from the DOS command line simply by typing its name, like any other DOS program.  A TNT DOS-Extender Lite program can also be run under DOS without a command line, using any of the many alternatives to COMMAND.COM such as the DOSSHELL in DOS 5 and higher.

By default, a protected-mode program modified by REBIND includes a real-mode "stub" program called GOLITE.  When one of these programs is started under DOS, an embedded copy of GOLITE.EXE actually runs initially.  GOLITE is called a stub because its sole job is to restart the program under TNT DOS-Extender Lite (TNTLITE.EXE).  In other words, if PHOO.EXE is produced by the commands:

C:\>cl phoo.c

C:\>rebind phoo

then running the program by typing its name:

C:\>phoo

is equivalent to typing TNTLITE followed by the name of the program:

C:\>tntlite phoo

This seems like no big deal:  after all, we've saved only seven letters and a space.  However, by eliminating the need to type those eight keystrokes, we have effectively hidden the DOS extender, and created the useful illusion that MS-DOS is an operating system that runs 32-bit protected-mode executables.

Though usually hidden, the key component of TNT DOS-Extender Lite is TNTLITE.EXE, which loads 32-bit Windows NT .EXE files under MS-DOS. The directory containing TNTLITE.EXE, for example C:\MSVC32S\PHARLAP\BIN, must be located somewhere on the DOS file-search path when you start an executable produced by REBIND.

TNTLITE.EXE puts the computer into protected mode and loads your protected-mode program.  Whenever your program makes DOS (INT 21h) or BIOS requests from protected mode (see BIOS Calls in Protected-Mode), the DOS extender quickly and invisibly does the work of sending the requests off to MS-DOS or the ROM BIOS, which are running in real mode.  Thus, TNT DOS-Extender Lite is 100% compatible with DOS:  it's not an emulation, but literally an extension of DOS.


Interoperability with Other System Software

While fully compatible with all the PC industry standards for protected mode and extended memory, TNTLITE does have a few modest requirements:

Your PC must have a 80386 or higher processor, at least 2 MB extended memory, and be running DOS 3.3 or later.

Visual C++, 32-Bit Edition must be installed on your hard disk.

If an expanded memory (EMS) handler is installed, it must follow the EMS 4.0 and VCPI specifications.  Almost all EMS managers shipping today are VCPI-compatible; if yours isn't, contact the manufacturer about upgrading to a new version.

Microsoft's EMM386, included with DOS 5.0 (and higher) and Windows 3.x, is VCPI-compatible, and can therefore be used with TNT DOS-Extender Lite.  However, the EMM386 NOEMS switch effectively disables VCPI support, and therefore cannot be used.  We recommend adding the switch FRAME=NONE, which uses no more Upper Memory Blocks than NOEMS, but which does allow VCPI-compatible clients to run.

If an extended memory (XMS) manager such as HIMEM.SYS is installed, we suggest setting a fairly large handle count.  For example:

DEVICE=C:\WIN30\HIMEM.SYS /NUMHANDLES=127

TNTLITE will run in the DOS compatibility boxes of Windows NT,   Windows 3.x, and OS/2 2.0, which provide DPMI support.

One important word of warning:  Even though you don't have to explicitly type "TNTLITE" to run an executable produced by REBIND under MS-DOS, the TNTLITE.EXE file is necessary.  The directory containing this file (for example C:\MSVC32S\PHARLAP\BIN) must be located somewhere on the DOS file-search path.  If GOLITE cannot find TNTLITE.EXE, it will display the message:

This program requires TNTLITE.EXE to be in your path.

If you receive this message when trying to run a TNT DOS-Extender Lite executable, ensure that your path is correct.  For example:

C:\TEST>set path=c:\bin;c:\msvc32s\pharlap\bin;c:\dos

Note that TNTLITE.EXE cannot be shipped to end-users.  In order to prepare programs for redistribution, you must purchase the TNT DOS-Extender Run-Time Kit (RTK), which lets you bind the TNT DOS-Extender plus any necessary DLLs into your application (an application may consist of multiple executable files, all of which are covered by a single license agreement).

With these details out of the way, protected-mode programs will run under MS-DOS.


The TNT Application Program Interface

TNT DOS-Extender Lite support is limited to those functions found in the C run-time library.  However, the full-blown TNT DOS-Extender SDK also supports protected-mode INT 21H calls, protected-mode BIOS calls, and many Win32 API functions from KERNEL32.


Error Messages

The most common TNTLITE error messages are described in the following list:

Related Topics:

Fatal error TNTLITE.1000: no executable file on command line

Fatal error TNTLITE.1020: unhandled exception occurred: <exception>

Fatal error TNTLITE.1040: cannot load DLL; MS-DOS error number <number>

Fatal error TNTLITE.1060: cannot start program; real-mode multitasking not supported

Fatal error TNTLITE.1070: stub is out of date

Fatal error TNTLITE.1080: <functionname> function called but not implemented

Fatal error TNTLITE.1170: cannot load <filename> at <hexaddress>: no fixups

Fatal error TNTLITE.1210: cannot load program : unknown error code <number>

Fatal error TNTLITE.1230: TNTLITE initialization failed

Fatal error TNTLITE.1250: command line error : environment variables nested too deeply at <envvar>

Fatal error TNTLITE.1310: command line error : command too long <command>

Fatal error TNTLITE.1350: cannot load program : file not found

Fatal error TNTLITE.1360: cannot load program : directory not found

Fatal error TNTLITE.1370: cannot load program : too many open files

Fatal error TNTLITE.1380: cannot load program : access denied to file

Fatal error TNTLITE.1390: cannot load program : out of memory

Fatal error TNTLITE.1400: cannot load program : invalid drive

Fatal error TNTLITE.1420: cannot load program : invalid or corrupted .EXE file

Fatal error TNTLITE.1450: Unimplemented function <function> called at <address>

Error TNTLITE.2010: invalid pointer passed to free or realloc

Warning TNTLITE.4020: out of file handles.  Increase value of FILES= statement in CONFIG.SYS

System Errors


Fatal error TNTLITE.1000: no executable file on command line

Fatal error TNTLITE.1000: no executable file on command line

Cause:	Your command line does not contain the name of an .EXE file.

Solution:	Specify an .EXE file for TNTLITE to load.


Fatal error TNTLITE.1020: unhandled exception occurred: <exception>

Fatal error TNTLITE.1020: unhandled exception occurred: <exception>

where <exception> is one of:

Divide by zero exception

Single-step interrupt

Parity error

Breakpoint interrupt

Overflow interrupt

Bounds exception

Invalid opcode exception

Coproc not present exception

Double fault exception

Coprocessor segment overrun

Invalid TSS exception

Segment not present fault

Stack fault

General protection fault

Page fault

Cause:	The most common exception is a General protection (GP) fault. If your program receives a GP fault, it violated the rules of protected mode (see What's So Protected About Protected Mode?).  There is a bug in your program, OR a piece of code which must be modified before it can be run in protected mode, OR you have unintentionally included real-mode code (for example, real-mode libraries) in the program.

In addition to the CPU register dump, TNTLITE displays the process ID (ProcessId), thread ID (ThreadId), and exception error code.  For GP faults, the error code generally bears some resemblance to an invalid selector that caused the fault (for example, an attempt to move the number B800h into ES will generate ERRORCODE=B800).

Solution:	Correct the bug that caused the exception, and try again.  If you need to catch the exception, rather than terminate the program with this message, use the structured exception handling mechanism.


Fatal error TNTLITE.1040: cannot load DLL; MS-DOS error number <number>

Fatal error TNTLITE.1040: cannot load DLL; MS-DOS error number <number>

Cause:	TNTLITE cannot load your program due to a DOS error.

 Solution:	Verify that the DLL and .EXE files have been built properly and that the DLL is on the PATH, and try again.


Fatal error TNTLITE.1060: cannot start program; real-mode multitasking not supported

Fatal error TNTLITE.1060: cannot start program; real-mode multitasking not supported

Cause:	You are attempting to execute more than one real-mode program concurrently.

Solution:	Do not attempt to run more than one real-mode program at a time.


Fatal error TNTLITE.1070: stub is out of date

Fatal error TNTLITE.1070: stub is out of date

Cause:	The files REBIND.EXE, TNTLITE.EXE, and GOLITE.EXE must all have the same version number.  The version of GOLITE you have does not match the other two files.

Solution:	 Rebind your program with the version of GOLITE that matches your REBIND.EXE and TNTLITE.EXE files.


Fatal error TNTLITE.1080: <functionname> function called but not implemented

Fatal error TNTLITE.1080: <functionname> function called but not implemented

Cause:	You have called a Win32 API function which has not yet been implemented.

Solution:	Try to find a workaround that does not use the unimplemented function.


Fatal error TNTLITE.1170: cannot load <filename> at <hexaddress>: no fixups

Fatal error TNTLITE.1170: cannot load <filename> at <hexaddress>: no fixups

Cause:	The file was linked with no fixups, and TNTLITE could not load the file at the specified base address.

Solution:	Relink the file with fixups, and try loading again.


Fatal error TNTLITE.1210: cannot load program : unknown error code <number>

Fatal error TNTLITE.1210: cannot load program : unknown error code <number>

Cause:	TNTLITE cannot load your protected-mode executable because it encountered an unknown error.

Solution:	The number specified in the message is a Windows NT error code.  Please consult the appropriate documentation for more information.


Fatal error TNTLITE.1230: TNTLITE initialization failed

Fatal error TNTLITE.1230: TNTLITE initialization failed

Cause:	This message usually follows another, more descriptive, message describing the cause of the initialization failure. The most common reason for failing is insufficient memory.

Solution:	Correct the problem identified in the previous message and try again.


Fatal error TNTLITE.1250: command line error : environment variables nested too deeply at <envvar>

Fatal error TNTLITE.1250: command line error : environment variables nested too deeply at <envvar>

Cause:	Environment variables optionally used by TNTLITE can themselves contain references to other environment variables, using the %envvar syntax.  However, there is a limit of four nested environment variables.  Most likely, you have an infinite loop.

Solution:	Don't nest more than four environment variables.


Fatal error TNTLITE.1310: command line error : command too long <command>

Fatal error TNTLITE.1310: command line error : command too long <command>

Cause:	The specified command line contains more than 511 characters.

Solution:	Break the command into multiple lines.


Fatal error TNTLITE.1350: cannot load program : file not found

Fatal error TNTLITE.1350: cannot load program : file not found

Cause:	TNTLITE cannot find an .EXE file with the specified name.

Solution:	Check your DOS PATH, and/or the spelling of the file name, and try again.


Fatal error TNTLITE.1360: cannot load program : directory not found

Fatal error TNTLITE.1360: cannot load program : directory not found

Cause:	TNTLITE cannot find the named subdirectory.

Solution:	Check your spelling, and/or make sure the subdirectory really exists, and try again.


Fatal error TNTLITE.1370: cannot load program : too many open files

Fatal error TNTLITE.1370: cannot load program : too many open files

Cause:	TNTLITE cannot load your protected-mode program because  there aren't enough free DOS file handles.

Solution:	Either increase the number of DOS file handles by increasing the FILES= count in CONFIG.SYS, or close some files.


Fatal error TNTLITE.1380: cannot load program : access denied to file

Fatal error TNTLITE.1380: cannot load program : access denied to file

Cause:	TNTLITE cannot load your protected-mode executable because the file cannot be accessed.  Most likely, this is because the file resides on a network server, and you don't have access rights to the file.

Solution:	Get your network/system administrator to give you access rights to the file, or try a different file.


Fatal error TNTLITE.1390: cannot load program : out of memory

Fatal error TNTLITE.1390: cannot load program : out of memory

Cause:	TNTLITE cannot load your protected-mode executable because all conventional and extended memory is exhausted.

Solution:	Remove some TSRs, or install more memory.


Fatal error TNTLITE.1400: cannot load program : invalid drive

Fatal error TNTLITE.1400: cannot load program : invalid drive

Cause:	TNTLITE cannot load the specified file because the drive letter is invalid.

Solution:	Specify a valid DOS drive letter.


Fatal error TNTLITE.1420: cannot load program : invalid or corrupted .EXE file

Fatal error TNTLITE.1420: cannot load program : invalid or corrupted .EXE file

Cause:	TNTLITE cannot load the specified file because it is not a valid .EXE file.  For example, typing TNTLITE HELLO.C instead of TNTLITE HELLO.EXE will result in this error message.

Solution:	Make sure that the file is a valid .EXE file, specify a different file, or relink the file.


Fatal error TNTLITE.1450: Unimplemented function <function> called at <address>

Fatal error TNTLITE.1450: Unimplemented function <function> called at <address>

Cause:	You have called a Win32 API function which has not yet been implemented.

Solution:	Try to find a workaround that does not use the unimplemented function.


Error TNTLITE.2010: invalid pointer passed to free or realloc

Error TNTLITE.2010: invalid pointer passed to free or realloc

Cause:	A call with a bad pointer was made to one of the following Win32 API functions:  LocalReAlloc(), LocalFree(), GlobalReAlloc(), or GlobalFree().

Solution:	Fix the bug that caused the invalid pointer.


Warning TNTLITE.4020: out of file handles.  Increase value of FILES= statement in CONFIG.SYS

Warning TNTLITE.4020: out of file handles.  Increase value of FILES= statement in CONFIG.SYS

Cause:	All the available file handles are in use


System Errors

You should get a System Error message only with a particularly bad crash.  If you regularly get a System Error, please contact Phar Lap technical support at (617) 661-1510.


Example Programs - Overview
Overview

TNT DOS-Extender Lite includes a number of example programs.  They are included in the MSVC32S\PHARLAP\EXAMPLES subdirectories.  There are four subdirectories:

FULLSCR -- Full-screen I/O

GRAPHICS -- Calling the Microsoft 16-bit Graphics Library

INTBENCH -- Several Popular Integer Benchmarks

MOUSE -- Calling the Real-Mode Mouse Driver from Protected Mode

Each subdirectory contains all the files needed to build the particular program.  To build any program, CD to that directory (e.g., C:\MSVC32S\PHARLAP\EXAMPLES\FULLSCR) and type MAKEALL.  This calls the MAKEALL.BAT file which, in turn, invokes Microsoft's NMAKE utility to build the program.  NMAKE uses either MAKEFILE or one of the *.MAK files in the directory.


FULLSCR -- Full-screen I/O

The EXAMPLES\FULLSCR directory contains a collection of subroutines and a driver program illustrating how to perform full-screen I/O from protected mode.

FULLSCR.C	Main program that determines how long it takes to write "hello world!" at random locations and in random colors all over the screen 30,000 times.

SCREEN.C	Collection of subroutines that perform the actual screen I/O.  As previously explained in What About Direct Screen Writes?, the TNT DOS-Extender Lite functions WriteRealMem() and ReadRealMem() are used to directly address the real-memory video buffers.  A short description of these functions follows:

ReadRealMem() offers an alternative to writing in-line assembly code to read real-mode memory locations.  The function copies bytes from the real-mode address space to the protected-mode address space.

VOID APIENTRY ReadRealMem(PVOID pProt, REALPTR pReal, DWORD Count);

where

pProt is a protected-mode pointer indicating where the data is to be copied to

pReal is a real-mode pointer indicating where the data is to be copied from

Count is the number of bytes to read.

WriteRealMem() is similar to ReadRealMem() except that it writes (i.e., copies to real-mode memory) instead of reading real-mode memory.

VOID APIENTRY WriteRealMem(REALPTR pReal, PVOID pProt, DWORD Count);

where

pReal is a real-mode pointer indicating where the data is to be copied to

pProt is a protected-mode pointer indicating where the data is to be copied from

Count is the number of bytes to write

These functions are contained in the TNTAPI.LIB library file.


GRAPHICS -- Calling the Microsoft 16-bit Graphics Library

The EXAMPLES\GRAPHICS directory contains a sample program that calls the Microsoft 16-bit real-mode graphics library.  This program demonstrates mixing real- and protected-mode code by placing each in separate executable files.  The GDEMO.EXE 32-bit protected-mode program loads the real-mode GSERVER.DRV program and then makes calls to the real-mode Microsoft C graphics library routines included in GSERVER.DRV.  All of the nitty-gritty interfacing details are handled in the "glue" routines, low-level code that performs the data manipulation and mode switching that allow us to call a real-mode library from protected mode.

GDEMO.C	The main protected-mode 32-bit C program that makes calls to real-mode Microsoft C graphics routines.

GGLUE.C	Protected-mode C glue code for handling cross-mode function calls.

PROT.ASM	Protected-mode assembly language glue code for handling cross-mode function calls.

GSERVER.C	The real-mode 16-bit C program that links in the graphics library.

GSERVER.H	A C header file that defines the communication protocol between the real- and protected-mode code.

REAL.ASM	Real-mode assembly language glue code for handling cross-mode function calls.

TNTAPI.H	A C header file that defines the TNT DOS-Extender Lite API.  The protected-mode glue uses TNT API functions for mixing real- and protected-mode code.

TNTAPI.LIB	The library file containing the TNT DOS-Extender Lite API library.

GDEMO.MAK	Make file for the 32-bit protected-mode main program.

GSERVER.MAK	Make file for the 16-bit real-mode graphics server.

While the files in this directory are specific to the 16-bit real-mode Microsoft graphics library, the techniques can be extrapolated to any real-mode library.  In particular, a different server and a different set of glue routines would be needed for interfacing to a different real-mode library.


INTBENCH -- Several Popular Integer Benchmarks

The EXAMPLES\INTBENCH directory contains a number of popular integer benchmark routines.  There are two C files in this directory:

INTBENCH.C	main program and the benchmark functions

DOBENCH.C	driver and timing routines for running the benchmarks

INTBENCH.C is compiled with the /Ogit optimization switch to enable global common subexpression optimizations, to generate intrinsic functions, and to minimize execution speed.  There are four benchmarks measured:

Fibonacci	computes the Fibonacci number of 24 in a loop that iterates 100 times; 24 is used because Fibonacci(24) is the largest Fibonacci number that can be computed in 16 bits.  (This program was originally developed for a 16-bit environment.)  It has not been changed so this program can be used to provide timing comparisons of 16-bit and 32-bit environments, as discussed in 32-bit vs. 16-bit Performance.

Sieve	finds all prime numbers less than 8190 in a loop that iterates 100 times.  The function is based on the Eratosthenes Sieve Prime Number Program from the January 1983 issue of BYTE magazine.

Sort	fills an array of 1000 random numbers then performs a  quick sort on the array; the process of filling and sorting the array is inside a loop that iterates 100 times.

DHRYSTONE 1.1	calls the Dhrystone benchmark program for C compilers in a loop that iterates 50,000 times.  We're using the January 1986 version of this program.

The program prints out a results summary table as it progresses.


MOUSE -- Calling the Real-Mode Mouse Driver from Protected Mode

MOUSE.C is a simple program that illustrates calling a real-mode interrupt handler from protected mode.  Once the program has made sure that a mouse is available, it displays the current mouse position as the mouse is moved around the screen.  Pressing any mouse button terminates the program.  This program uses in-line assembly language to call the mouse driver.


Bibliography

Developers using TNT DOS-Extender Lite may be interested in the following books:

The second edition of the book Extending DOS, edited by Ray Duncan. This book describes 16- and 32-bit protected-mode DOS extenders, the VCPI and DPMI specifications, and other topics in protected-mode DOS programming:

Ray Duncan et al., Extending DOS: A Programmer's Guide to Protected-Mode DOS, Second Edition, Reading MA: Addison-Wesley, 1991, 538 pp., ISBN 0-201-56798-9.

Al Williams' survey of DOS Extenders, DOS and Windows Protected Mode. This book describes nearly every 16- and 32-bit DOS extender available, with working examples of how to write programs for each one.

Al Williams, DOS and Windows Protected Mode: Programming with DOS Extenders in C, Reading MA: Addison-Wesley, 1993, 503 pp., ISBN 0-201-63218-7.