Распределение XMS в 16-битном DOS - PullRequest
2 голосов
/ 01 декабря 2010

Пожалуйста, прости мои попытки некромантии, но мне действительно нужно написать код для 16-битной DOS (!). Я должен убедиться, что часть программного обеспечения работает правильно при сборке для 16-битной платформы, и я обнаружил, что на наших рабочих станциях XP действительно могут работать 16-битные приложения DOS, что позволяет использовать существующую систему пакетного тестирования.

В любом случае программное обеспечение состоит из одной библиотеки и одной базы данных. Меньшие базы данных (до ~ 150 КБ) могут быть определены как статический глобальный массив или считаны из файла в буфер, выделенный с помощью halloc(), поэтому я вполне уверен, что библиотека и инструмент тестирования построены правильно.

Однако у нас также есть несколько больших баз данных для тестирования, до ~ 1,8 МБ. Это слишком велико для нормального распределения, поэтому я написал небольшую вспомогательную библиотеку для выделения памяти XMS. Благодаря этому я могу успешно распределять, использовать (т.е. записывать и читать данные) и освобождать до 16 МБ данных в небольшой игрушечной программе. Однако при использовании средств 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. Признается ли ссылка на API-интерфейс XMS, которую я нашел, при работе в Windows XP, или есть более новая версия, на которую я должен ссылаться?
  3. Могу ли я испортить состояние системы с помощью встроенного ассемблера? Как мне решить это?
  4. У вас есть идея получше, как это решить? :-) DOS-экстендерам, похоже, требуется 32-битный режим, весь смысл этого упражнения в использовании 16-битного режима.

Ответы [ 3 ]

1 голос
/ 16 декабря 2010

Я вижу две проблемы.

1-й: Убедитесь, что ваш компилятор использует дальний вызов вместо ближнего, иначе вы перейдете к неправильному сегменту и выполните неизвестный код и, возможно, сгенерируете недопустимый код операции.что, кажется, и происходит.Попробуйте "call far [XMSControl]" в своем коде, если ваш компилятор по умолчанию использует ближние вызовы.

2-й: NTVDM.EXE выполняет код в режиме виртуального 86, а не в реальном режиме.Несмотря на то, что Windows XP поддерживает 16-битные приложения, она имеет ОГРАНИЧЕННУЮ поддержку.Из-за этого у вас могут возникнуть другие проблемы с вашей базой данных.Например, у вас не будет доступа к USB, поэтому вы не сможете печатать на USB-принтере и должны будете использовать старый принтер в стиле параллельного порта.Удачи в поиске одного из тех, кто использовался при продаже гаража ...

1 голос
/ 08 февраля 2011

Ack, я должен ответить на свой собственный вопрос.

Хотя можно выделить относительно большой объем памяти с помощью подпрограмм XMS, невозможно адрес это с 16-битными инструкциями.Возвращаемым значением является 32-битный линейный адрес, и если я правильно понял, просто невозможно использовать его из 16-битной среды.

должно быть возможным для использования этого блока памяти, отображая его часть в некоторый локальный буфер памяти ниже адресуемого предела в 1 Мб и перемещая окно, подобно тому, как работает EMS, ноэто просто не стоит проблем.

0 голосов
/ 01 декабря 2010

'0f 04 10 0e 51' - недопустимая инструкция, которую вы пытаетесь выполнить.

Я проверил это и не вижу инструкции x86 с этим кодом 0F 04

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

Поэтому вам нужно найти, какой код генерирует такой недействительныйИнструкция и исправить.

Что касается памяти, я всегда думал, что EMS проще в использовании, чем XMS, но я могу ошибаться.

...