16 位 DOS 中的 XMS 分配

发布于 2024-10-05 13:15:56 字数 5732 浏览 1 评论 0原文

请原谅我尝试死灵术,但我实际上需要为 16 位 DOS 编写一些代码(!)。我必须验证一个软件在为 16 位平台构建时是否正确执行,我发现我们的 XP 工作站实际上可以运行 16 位 DOS 应用程序,这使得使用现有的批量测试系统成为可能。

无论如何,该软件由一个库和一个数据库组成。较小的数据库(最多约 150kB)可以定义为静态全局数组,也可以从文件读取到使用 Halloc() 分配的缓冲区,因此我对构建库和测试工具相当有信心正确。

然而,我们还有一些更大的数据库需要测试,高达~1.8Mb。这太大了,无法正常分配,因此我编写了一个小型支持库来分配 XMS 内存。有了这个,我可以在一个小玩具程序中成功分配、使用(即写入和读取数据)和释放最多 16Mb 的数据。然而,当在“真实”应用程序中使用 XMS 设施时,我收到以下错误:

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

谷歌搜索此错误几乎没有给出相关结果,这种类型的错误似乎通常归咎于其他恶意软件。

代码库是严格的 C90,当前用于 DOS 构建的编译器是使用“大”内存模型的 OpenWatcom 1.9。构建时没有警告或错误。

XMS支持库如下,错误似乎发生在调用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);
}

一些具体问题:

  1. 在哪里可以找到有关错误消息的更多信息(我猜OP意味着操作码,但是其他字段呢?
  2. )我发现 XMS API 参考在 Windows XP 上运行时仍然适用,或者我应该参考一些较新的版本吗?
  3. 我可以用内联汇编器搞砸系统状态吗?我应该如何解决这个问题?
  4. 您对如何解决这个问题有更好的想法吗? :-) DOS 扩展器似乎需要 32 位模式,本练习的重点是使用 16 位模式。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

以为你会在 2024-10-12 13:15:56

我看到两个问题。

第一:确保您的编译器使用远调用而不是近调用,否则您将跳转到错误的段并执行未知代码,并可能生成无效的操作码...这似乎正在发生。如果您的编译器默认为近调用,请尝试在代码中“调用远 [XMSControl]”。

第二:NTVDM.EXE 在虚拟 86 模式而不是真正的实模式下运行代码。虽然 Windows XP 确实支持 16 位应用程序,但它的支持有限。因此,您的数据库可能会遇到其他问题。例如,您将无法访问 USB,因此您无法在 USB 打印机上进行打印,并且需要使用旧的并行端口式打印机。祝你好运,找到车库拍卖中使用的其中一个...

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...

妄司 2024-10-12 13:15:56

嗯,我必须回答我自己的问题。

虽然可以使用 XMS 例程分配相对大量的内存,但不可能使用 16 位指令寻址。返回值是一个 32 位线性地址,如果我正确理解了这一点,则根本不可能在 16 位环境中使用它。

应该可以通过将内存块的一部分映射到低于 1Mb 可寻址限制的某个本地内存缓冲区并移动窗口来使用该内存块,就像 EMS 的工作方式一样,但这根本不值得麻烦了。

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.

如痴如狂 2024-10-12 13:15:56

“0f 04 10 0e 51”是您尝试执行的无效指令本身。

我已经检查过了,我没有看到带有此代码 0F 04

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

因此,您需要找到哪些代码生成了此类无效指令并修复它。

至于内存,我一直认为EMS比XMS好用,但我可能是错的。

'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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文