Распределение XMS в 16-битной DOS
Вопрос
Пожалуйста, простите мои попытки некромантии, но мне действительно нужно написать код для 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
Поиск в Google этой ошибки дал мало значимых результатов; похоже, что в этом типе ошибки обычно виноваты разные вредоносные программы.
Базовый код — строгий 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);
}
Несколько конкретных вопросов:
- Где я могу найти дополнительную информацию о сообщении об ошибке (я думаю, OP означает код операции, но как насчет других полей?)
- Действует ли найденная мной ссылка на XMS API при работе в Windows XP или есть какая-то более новая версия, на которую мне следует ссылаться?
- Могу ли я испортить состояние системы с помощью встроенного ассемблера?Как мне это решить?
- Есть ли у вас идеи получше, как это решить?:-) Похоже, что расширители DOS требуют 32-битного режима, весь смысл этого упражнения — в использовании 16-битного режима.
Решение 2
ACK, я должен ответить на свой собственный вопрос.
Пока можно распределять относительно большие объемы памяти, используя процедуры XMS, невозможно. адрес это с 16-битными инструкциями. Возвратное значение представляет собой 32-битный линейный адрес, и если я правильно понял это, просто невозможно использовать его из 16-битной среды.
Это должен Будьте возможны, чтобы использовать этот блок памяти, отображая его часть с каким -то локальным буфером памяти ниже 1MB -адресуемого предела и переместите окно, очень похоже на то, как работает EMS, но это просто не стоит проблем.
Другие советы
Я вижу две проблемы.
1 -й: убедитесь, что ваш компилятор использует дальний вызов вместо ближнего вызова, иначе вы перейдете к неправильному сегменту и выполняете неизвестный код и, возможно, генерируя неверный оплот ... что, кажется, происходит. Попробуйте «Позвоните в FAR [XMSControl]» в вашем коде, если ваш компилятор по умолчанию по умолчанию на ближние вызовы.
2nd: NTVDM.Exe запускает код в режиме Virtual 86, а не в реальном режиме. Хотя это правда, что Windows XP поддерживает 16 -битные приложения, он имеет ограниченную поддержку. Вы можете столкнуться с другими проблемами с вашей базой данных в будущем из -за этого. Например, вы не сможете получить доступ к USB, поэтому вы не можете печатать на USB -принтере и вам нужно будет использовать старый принтер в стиле параллельного порта. Удачи найди один из тех, кто использовался на продаже гаража ...
«0f 04 10 0e 51» — это недопустимая инструкция, которую вы пытаетесь выполнить.
Я проверил это и не вижу инструкции x86 с этим кодом 0F 04
http://ref.x86asm.net/geek.html#x0F04
Поэтому вам нужно найти, какой код генерирует такую неверную инструкцию, и исправить ее.
Что касается памяти, то я всегда думал, что EMS проще в использовании, чем XMS, но могу ошибаться.