Pergunta

Please forgive my attempts at necromancy, but I actually need to write some code for 16-bit DOS (!). I have to verify that a piece of software executes correctly when built for a 16-bit platform, and I discovered that our XP workstations can actually run 16-bit DOS apps, which makes it possible to use the existing batch test system.

In any case, the software consists of one library and one database. Smaller databases (up to ~150kB) can either be defined as a static global array or read from file to a buffer allocated with halloc(), so I am fairly confident that the library and test tool is built correctly.

However, we also have some larger databases to test, up to ~1.8Mb. This is too large to allocate normally, so I wrote a small support library to allocate XMS memory. With this, I can successfully allocate, use (i.e. write and read data) and free up to 16Mb of data in a small toy program. However, when using the XMS facilities in the "real" application, I get the following error:

The NTVDM CPU has encountered an illegal instruction.
CS:0000 IP:00ba OP:0f 04 10 0e 51

Googling on this error gave little relevant results, this type of error seems to be usually blamed on miscellaneous malware.

The code base is strict C90, the compiler currently used for DOS builds is OpenWatcom 1.9 using the "large" memory model. No warnings or errors while building.

The XMS support library follows, the error seems to occurs after the call to xmsmalloc():

/* This file implements rudimentary XMS memory handling.
 * Documentation on the XMS API was found on http://www.qzx.com/pc-gpe/xms30.txt
 */

#include <stddef.h> /* Definition of NULL */
#include <limits.h> /* Definition of UINT_MAX */
#include <stdio.h>  /* fprintf and (FILE *) */

/* Allow external configuration of maximum concurrent XMS allocations */
#ifndef MAX_XMS_ALLOCATIONS
#define MAX_XMS_ALLOCATIONS 4
#endif

/* Address of the XMS driver */
static long XMSControl;

/* Mapping of XMS handle <-> normal pointer */
typedef struct {
    unsigned int XMSHandle;
    void huge * XMSPointer; 
} XMSHandleMap;

static XMSHandleMap allocMap[MAX_XMS_ALLOCATIONS];

/* Set up the XMS driver, returns 0 on success and non-zero on failure */
static int initxms(void)
{
    char XMSStatus = 0;

    if ( XMSControl == 0 )
    {
        __asm {
        ; Is an XMS driver installed?
            mov ax,4300h
            int 2Fh
            mov [XMSStatus], al
        }

        if ( XMSStatus == 0x80 )
        {
            __asm {
            ; Get the address of the driver control function
                mov ax,4310h
                int 2Fh
                mov word ptr [XMSControl]  ,bx
                mov word ptr [XMSControl+2],es
            }
        }
    }

    return ( XMSControl == 0 );
}

/* Allocate a slab of memory from XMS */
void huge * xmsmalloc(long unsigned int size)
{
    unsigned int kB;
    unsigned int XMSStatus = 0;
    unsigned int XMSHandle = 0;
    void huge * XMSPointer = NULL;
    int n;

    /* If we can not initialize XMS, the allocation fails */
    if ( initxms() )
        return NULL;

    /* It is not possible to allocate more kilobytes than a 16-bit register can hold :-) */
    if ( size / 1024 > UINT_MAX )
        return NULL;

    /* Get the next free entry in the handle <-> pointer mapping */
    for ( n = 0; n < MAX_XMS_ALLOCATIONS; n++ )
    {
        if ( allocMap[n].XMSPointer == NULL )
            break;
    }

    if ( n == MAX_XMS_ALLOCATIONS )
        return NULL;

    kB = size / 1024 + (size % 1024 > 0);

    __asm {
    ; Allocate [kB] kilobytes of XMS memory
        mov ah, 09h
        mov dx, [kB]
        call [XMSControl]
        mov [XMSStatus], ax
        mov [XMSHandle], dx
    }

    /* Check if XMS allocation failed */
    if ( XMSStatus == 0)
        return NULL;

    __asm {
    ; Convert XMS handle to normal pointer
        mov ah, 0Ch
        mov dx, [XMSHandle]
        call [XMSControl]
        mov [XMSStatus], ax

        mov word ptr [XMSPointer],  bx 
        mov word ptr [XMSPointer+2],dx
    }

    if ( XMSStatus == 0 )
    {
        /* Lock failed, deallocate the handle */
        __asm {
        ; Free XMS handle
            mov ah, 0Ah
            mov dx, [XMSHandle]
            call [XMSControl]

        ; Return value is not really interesting 
        ;   mov [XMSStatus], ax
        }
        return NULL;
    }

    /* Create an entry in the handle <-> pointer mapping */
    allocMap[n].XMSHandle = XMSHandle;
    allocMap[n].XMSPointer = XMSPointer;

    return XMSPointer;
}

/* Free a pointer allocated with xmsalloc */
void xmsfree(void huge * XMSPointer)
{
    int n;

    if ( XMSPointer == NULL )
        return;

    if ( initxms() ) 
        return;

    for ( n = 0; n < MAX_XMS_ALLOCATIONS; n++ )
    {
        if ( allocMap[n].XMSPointer == XMSPointer )
        {
            int XMSHandle = allocMap[n].XMSHandle;

            __asm {
            ; Unlock handle so we can free the memory block
                mov ah, 0Dh
                mov dx, [XMSHandle]
                call [XMSControl]

            ; Free XMS memory
                mov ah, 0Ah
                mov dx, [XMSHandle]
                call [XMSControl]

            ; Return value ignored
            }

            /* Clear handle <-> pointer map entry so it can be reused */
            allocMap[n].XMSHandle = 0;
            allocMap[n].XMSPointer = NULL;

            return;
        }
    }
}

/* Write a memory report for debugging purposes */
void xmsreport(FILE * stream)
{
    int XMSVersionNumber = 0;
    int XMSLargestBlock = 0;
    int XMSTotal = 0;

    if ( initxms() ) 
    {
        puts("Could not initialize XMS Driver!");
        return;
    }

    __asm {
    ; Get the driver version number
        mov ah,00h
        call [XMSControl] ; Get XMS Version Number
        mov [XMSVersionNumber], ax

    ; Get the amount of free XMS memory
        mov ah, 08h
        call [XMSControl]
        mov [XMSLargestBlock], ax
        mov [XMSTotal], dx
    }

    fprintf(stream, "XMS Version number: %d\n", XMSVersionNumber);
    fprintf(stream, "Largest available block: %d kB (%d kB total)\n", XMSLargestBlock, XMSTotal);
}

Some concrete questions:

  1. Where can I find more information about the error message (I guess OP means opcode, but what about the other fields?)
  2. Does the XMS API reference I found still apply when running on Windows XP, or is there some newer version I should reference?
  3. Could I screwed up the system state with the inline assembler? How should I resolve that?
  4. Do you have any better idea on how to solve this? :-) DOS extenders seem to require 32-bit mode, the whole point of this exercise is using 16-bit mode.
Foi útil?

Solução 2

Ack, I have to answer my own question.

While it is possible to allocate relatively large amounts of memory using the XMS routines, it is not possible to address it with 16-bit instructions. The return value is a 32-bit linear address, and if I have understood this correctly it is simply not possible to use that from a 16-bit environment.

It should be possible to use that memory block by mapping a part of it to some local memory buffer below the 1Mb addressable limit and move the window around, much like how EMS works, but that is simply not worth the trouble.

Outras dicas

I see two problems.

1st: Make sure your compiler is using a far call instead of a near call otherwise you will jump to the wrong segment and execute unknown code and possably generating an invalid opcode... which is what seems to be happening. Try "call far [XMSControl]" in your code if your compiler defaults to near calls.

2nd: NTVDM.EXE runs code in virtual 86 mode and not true real mode. While it is true that windows XP supports 16 bit apps, it has LIMITED support. You may run into other problems with your database down the line because of that. For example you wont be able to access USB, so you can't print on a USB printer and will need to use an old parallel port style printer. Good luck findign one of those used at a garage sale...

'0f 04 10 0e 51' is invalid instruction itself you are trying to execute.

I've checked it, and I don't see x86 instruction with this code 0F 04

http://ref.x86asm.net/geek.html#x0F04

So you need to find which code generate such invalid instruction and fix it.

As for the memory, I've always thought that EMS is easier to use than XMS, but I may be wrong.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top