C++应用程序访问扩充内存(EMS)
摘要:通过调用INT 67H中断,编制了访问扩充内存C++接口程序(函数),使应用程序访问扩充内存同常规内存一样简单。 关键词:扩充内存,C++,INT 67H,EMS
应用程序运行时,由于受到PC计算机硬件限制,只能访问常规内存,不能访问扩充内存,编制占用较多内存的应用程序,通常采用内存覆盖技术,这不仅增加了程序编制的难度,而且有些问题使用常规内存无法解决,如果应用程序能访问扩充内存,程序运行受内存限制的问题将得到解决。笔者通过实践,在C++中实现了应用程序访问扩充内存。
1.扩充内存调用原理
LIM(Lotus/Intel/Microsoft)
EMS(扩充内存使用规范)允许程序使用比在实模式下直接寻址内存更多的内存到堆-切换内存,其功能调用由中断67H实现。INT 67H有关的功能见表1。
功能号 功能 入口参数 出口参数 注释
40H 获取管理程序的状态 AH=40H AH=错误代码
AH=00H成功 确定扩充内存硬件是否正常工作
42H 分配页数 AH=42H AH=错误代码
AH=00H成功
BX=未分配的页数
DX=总页数 确定有多少16KB扩充内存页可供分配及系统中存在的总页数
43H 获取句柄并分配内存 AH=43H
BX=要分配的逻辑页数 AH=错误代码
AH=00H成功
DX=句柄 请求若干16KB的逻辑页并将它们同一个独特的今后将用以处理这些页的标识符联系起来
45H 释放句柄和内存 AH=45H
DX=EMS句柄 AH=错误代码
AH=00H成功 释放前面使之同已扩展的内存的标识符相联系的页并使该标识符无效
57H 移动交换内存 AH=57H
AL=子功能
00H移动内存
01H交换内存
DS:SI=描述源及目的的结构(见表2) AH=错误代码
AH=00H成功 拷贝或交换内存区域的内容,源内存与目的内存二者均可为常规或扩充内存
表1 INT 67H中断有关功能调用
表2 INT 67H功能57H内存操作结构描述
偏移 大小 描述
00H DWORD 以字节计的区域长度
04H BYTE 源内存类型,00H--常规内存,01H--扩充内存
05H WORD 源句柄,0000H为常规内存
07H WORD 源初始偏移,如为扩充内存,则在页內,如为常规内存,则在段内
09H WORD 源初始段(常规内存)或逻辑页(扩充内存)
0BH BYTE 目的内存类型,00H--常规内存,01H--扩充内存
0CH WORD 目的句柄,0000H为常规内存
0EH WORD 目的初始偏移,如为扩充内存,则在页內,如为常规内存,则在段内
10H WORD 目的初始段(常规内存)或逻辑页(扩充内存)
表2为 INT 67H功能57H内存操作结构描述。
扩充内存使用规范EMS为,在常规内存开辟一缓存页框对扩充内存进行读写操作,此页框为若干个(通常为4个)16KB的页面,每个页面分别映射扩充内存的某逻辑页面,对常规内存缓存页框的操作结果,由驻留常规内存中的扩充内存管理程序EMM自动映射到相应的扩充内存中。
对扩充内存的操作均通过调用INT 67H完成,具体操作由寄存器AH中装入的功能号确定。
在DOS下使用扩充内存,必须加载扩充内存管理程序HIEMM.SYS和EMM386.EXE。
1. 扩充内存接口程序
扩充内存接口程序是用C++编制的,其中包括:
1. 扩充内存分配函数mallocEMS
2. 扩充内存释放函数freeEMS
3. 常规内存拷贝到扩充内存函数memcpyEMS
4. 扩充内存拷贝到常规内存函数memcpyEMS(重载)
5. 扩充内存拷贝到扩充内存函数memcpyEMS(重载)
6. 存一个字到扩充内存函数pokeEMS
7. 取扩充内存一个字函数pokeEMS
8. 存一个字节到扩充内存函数pokebEMS
9. 取扩充内存一个字节函数peekbEMS
接口程序EM.HPP如下:
#include<stdio.h>
#include<dos.h>
// 定义用于扩充内存操作的结构EMS
typedef struct
{ int handle; // 扩充内存操作句柄
unsigned ds,si; // 存放结构SEMS的段地址和偏移地址
} EMS;
// 定义用于INT 67H功能57H操作的结构SEMS
typedef struct
{ long size; // 拷贝以字节计的扩充内存区域长度
char type0; // 源内存类型,00H--常规内存,01H--扩充内存
int handle0; // 源内存句柄,0000H--常规内存
unsigned offset0; // 源初始偏移,若为常规内存则在段内,若为扩充则在页内
unsigned segment0; // 源初始段(常规内存),或逻辑页(扩充内存)
char type1; // 目的内存类型,00H--常规内存,01H--扩充内存
int handle1; // 目的内存句柄,0000H--常规内存
unsigned offset1; // 目的初始偏移,若为常规内存则在段内,若为扩充则在页内
unsigned segment1; // 目的初始段(常规内存),或逻辑页(扩充内存)
} SEMS;
// 定义用于调用有关INT 67H中断函数的类INT67H
class INT67H
{ private:
REGS r; // 伪寄存器变量
SREGS rs; // 伪段寄存器变量
// 中断67H函数
int int67h(void) // 参数:无
{ int86(0x67,&r,&r); // INT 67H
return r.h.ah; // 返回值:0无错,非0错误码
}
public:
// 得到扩充内存状态函数
int getstatus(void) // 参数:无
{ r.h.ah=0x40; // 中断67H功能40H
return int67h(); // 返回值:0无错,非0出错
}
// 得到扩充内存可用页数函数
unsigned getpages(void) // 参数:无
{ r.h.ah=0x42; // 中断67H功能42H
int67h();
return r.x.bx; // 返回值:扩充内存可用页数
}
// 分配扩充内存页并得到句柄函数
int allocatepages(int np) // 参数:np--分配扩充内存页数
{ r.h.ah=0x43; // 中断67H功能43H
r.x.bx=np;
int67h();
return r.x.dx; // 返回值:扩充内存页句柄
}
// 解除EMS页分配并释放句柄函数
void deallocatepages(int h) // 参数:h--扩充内存页句柄
{ r.h.ah=0x45; // 中断67H功能45H
r.x.dx=h;
int67h();
}
// 移动扩充内存函数
void movememory(EMS *p) // 参数:p--EMS指针
{ r.h.ah=0x57; // 中断67H功能57H
r.h.al=0x00; // 子功能00H--移动内存区域
r.x.si=p->si; // 描述源及目的结构的偏移地址
rs.ds=p->ds; // 描述源及目的结构的段地址
int86x(0x67,&r,&r,&rs); // INT 67H
}
};
/* 扩充内存分配函数mallocEMS
参数: n--申请扩充内存的字节数(long int型)
返回: EMS结构指针 */
EMS *mallocEMS(long n)
{ EMS *p=NULL;
SEMS *ps;
INT67H int67h;
unsigned np;
if(!int67h.getstatus()) // 判断扩充内存管理程序是否正常工作
if(!((np=(unsigned)((n-1)/0x4000+1))>int67h.getpages())) // 判断是否有足够的扩充内存页
if((p=new EMS)!=NULL) // 申请一存放EMS结构的内存
if(!(ps=new SEMS)) // 申请一存放SEMS结构的内存
{ delete p;
p=NULL;
}
else
{ p->handle=int67h.allocatepages(np); // 获得扩充内存句柄
p->ds=FP_SEG(ps); // 获得存放SEMS结构的段地址
p->si=FP_OFF(ps); // 获得存放SEMS结构的偏移地址
}
return p;
}
/* 扩充内存释放函数freeEMS
参数: n--EMS结构指针(EMS *型)
返回: 无 */
void freeEMS(EMS *p)
{ INT67H int67h;
if(p) // 辨别是否为已分配的扩充内存
{ int67h.deallocatepages(p->handle); // 解除句柄对应的扩充内存
delete (SEMS *)MK_FP(p->ds,p->si); // 回收SEMS结构占用的内存
delete p; // 释放EMS结构占用的内存
}
}
/* 常规内存拷贝到扩充内存函数memcpyEMS
参数: pdest--目的扩充内存EMS结构指针(EMS *型)
ldest--目的扩充内存逻辑地址(long int型)
memsrc--源常规内存指针(const void far *型)
n--拷贝内存区域长度(long int型)
返回: 无 */
void memcpyEMS(EMS *pdest,long ldest,const void far *memsrc,long n)
{ SEMS *ps=(SEMS *)MK_FP(pdest->ds,pdest->si); // SEMS结构指针
INT67H int67h;
ps->size=n; // 内存区域长度
ps->type0=0x00; // 源常规内存
ps->handle0=0x0000; // 源常规内存句柄
ps->offset0=FP_OFF(memsrc); // 源常规内存初始偏移地址
ps->segment0=FP_SEG(memsrc); // 源常规内存初始段地址
ps->type1=0x01; // 目的扩充内存
ps->handle1=pdest->handle; // 目的扩充内存句柄
ps->offset1=(unsigned)(ldest%0x4000); // 目的扩充内存初始逻辑偏移
ps->segment1=(unsigned)(ldest/0x4000); // 目的扩充内存初始逻辑页
int67h.movememory(pdest); // 常规内存拷贝到扩充内存
}
/* 扩充内存拷贝到常规内存函数memcpyEMS(重载)
参数: memdest--目的常规内存指针(void far *型)
psrc--源扩充内存EMS结构指针(EMS *型)
lsrc--源扩充内存逻辑地址(long int型)
n--拷贝内存区域长度(long int型)
返回: 目的常规内存指针(void far *型) */
void far *memcpyEMS(void far *memdest,EMS *psrc,long lsrc,long n)
{ SEMS *ps=(SEMS *)MK_FP(psrc->ds,psrc->si); // SEMS结构指针
INT67H int67h;
ps->size=n; // 内存区域长度
ps->type0=0x01; // 源扩充内存
ps->handle0=psrc->handle; // 源扩充内存句柄
ps->offset0=(unsigned)(lsrc%0x4000); // 源扩充内存初始逻辑偏移
ps->segment0=(unsigned)(lsrc/0x4000); // 源扩充内存初始逻辑页
ps->type1=0x00; // 目的常规内存
ps->handle1=0x0000; // 目的常规内存句柄
ps->offset1=FP_OFF(memdest); // 目的常规内存初始偏移地址
ps->segment1=FP_SEG(memdest); // 目的常规内存初始段地址
int67h.movememory(psrc); // 扩充内存拷贝到常规内存
return memdest;
}
/* 扩充内存拷贝到扩充内存函数memcpyEMS(重载)
参数: pdest--目的扩充内存EMS结构指针(EMS *型)
ldest--目的扩充内存逻辑地址(long int型)
psrc--源扩充内存EMS结构指针(EMS *型)
lsrc--源扩充内存逻辑地址(long int型)
n--拷贝内存区域长度(long int型)
返回: 无 */
void memcpyEMS(EMS *pdest,long ldest,EMS *psrc,long lsrc,long n)
{ SEMS *ps=(SEMS *)MK_FP(psrc->ds,psrc->si); // SEMS结构指针
INT67H int67h;
ps->size=n; // 内存区域长度
ps->type0=0x01; // 源扩充内存
ps->handle0=psrc->handle; // 源扩充内存句柄
ps->offset0=(unsigned)(lsrc%0x4000); // 源扩充内存初始逻辑偏移
ps->segment0=(unsigned)(lsrc/0x4000); // 源扩充内存初始逻辑页
ps->type1=0x01; // 目的扩充内存
ps->handle1=pdest->handle; // 目的扩充内存句柄
ps->offset1=(unsigned)(ldest%0x4000); // 目的扩充内存初始偏移地址
ps->segment1=(unsigned)(ldest/0x4000); // 目的扩充内存初始段地址
int67h.movememory(psrc); // 扩充内存拷贝到扩充内存
}
/* 存一个字到扩充内存函数pokeEMS
参数: p--扩充内存EMS结构指针(EMS *型)
locate--扩充内存逻辑地址(long int型)
value--要存储的2字节(int型)
返回: 无 */
void pokeEMS(EMS *p,long locate,int value)
{ memcpyEMS(p,locate,&value,sizeof(int));
}
/* 取扩充内存一个字函数pokeEMS
参数: p--扩充内存EMS结构指针(EMS *型)
locate--扩充内存逻辑地址(long int型)
返回: 取到的2字节(int型) */
int peekEMS(EMS *p,long locate)
{ int value;
return *(int *)memcpyEMS(&value,p,locate,sizeof(int));
}
/* 存一个字节到扩充内存函数pokebEMS
参数: p--扩充内存EMS结构指针(EMS *型)
locate--扩充内存逻辑地址(long int型)
value--要存储的1字节(char型)
返回: 无 */
void pokebEMS(EMS *p,long locate,char value)
{ memcpyEMS(p,locate,&value,sizeof(char));
}
/* 取扩充内存一个字节函数peekbEMS
参数: p--扩充内存EMS结构指针(EMS *型)
locate--扩充内存逻辑地址(long int型)
返回: 取到的1字节(char型) */
char peekbEMS(EMS *p,long locate)
{ char value;
return *(char *)memcpyEMS(&value,p,locate,sizeof(char));
}
在C++应用程序中使用扩充内存,需要导入文件EM.HPP,即在源程序前插入下列1行:
#include “EM.HPP”
文件EM.HPP应与C++应用程序在同一目录中。
在应用程序中对扩充内存操作过程为:
1. 调用扩充内存分配函数mallocEMS,申请指定大小的扩充内存,得到对扩充内存操作的EMS指针,指针非空表明扩充内存分配成功;
2.
调用有关扩充内存操作函数,如:有关扩充内存拷贝函数memcpyEMS,存一个字到扩充内存函数pokeEMS,取扩充内存一个字函数peekEMS,存一个字节到扩充内存函数pokebEMS,取扩充内存一个字节函数peekbEMS;
3. 调用扩充内存释放函数freeEMS,释放扩充内存。
程序用Turbo C++ 3.0大模式编译并与应用程序连接,在WINDOWS 95/98的MS-DOS方式下运行通过。