题
请原谅我对死灵的尝试,但实际上我需要为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);
}
一些具体问题:
- 我在哪里可以找到有关错误消息的更多信息(我猜OP表示OPCODE,但是其他字段呢?)
- 在Windows XP上运行时,我发现的XMS API参考是否仍适用,或者我应该引用一些较新的版本?
- 我可以用内联汇编器拧紧系统状态吗?我应该如何解决?
- 您对如何解决这个问题有更好的想法吗? :-) DOS扩展器似乎需要32位模式,此练习的全部要点是使用16位模式。
解决方案 2
ACK,我必须回答自己的问题。
虽然有可能 分配 使用XMS例程相对较大的内存,不可能 地址 它带有16位说明。返回值是一个32位线性地址,如果我正确理解了这一点,则根本无法从16位环境中使用它。
它 应该 可以通过将其一部分映射到1MB可寻址限制以下的某些本地内存缓冲区并将窗口移动到周围的某些本地内存缓冲区,从而使用该内存块,就像EMS的工作原理一样,但这根本不值得。
其他提示
我看到两个问题。
第一:确保您的编译器正在使用遥远的呼叫而不是近呼叫,否则您会跳到错误的片段并执行未知代码并可能生成无效的操作码...这似乎是发生的。如果您的编译器默认为近呼叫,请尝试在代码中使用“远[XMSCONTROL]”。
第二:ntvdm.exe以虚拟86模式运行代码,而不是真实的真实模式。虽然Windows XP确实支持16位应用程序,但它的支持有限。因此,您可能会遇到数据库的其他问题。例如,您无法访问USB,因此您无法在USB打印机上打印,并且需要使用旧的并行端口样式打印机。祝您好运找到车库销售中使用的人之一...
'0f 04 10 0E 51'是您要执行的指令本身无效。
我已经检查了它,但我看不到此代码0f 04的x86指令
http://ref.x86asm.net/geek.html#x0f04
因此,您需要找到哪种代码生成这种无效的指令并修复它。
至于内存,我一直认为EMS比XMS更易于使用,但我可能错了。