DOS中使用扩展内存一 HIMEM. SYS 和 XMS扩展内存(extended memory)是指超过8086的1M限制的内存,在使用之前, 必须有扩展内存驱动程序,MS-DOS 7. 10操作系统带有这个程序在config, sys 文件中要加上一句话:DEVTCE=C:\D0S71\HTMEM. SYS,然后就能在程序中访问扩 展内存了XMS是扩展内存管理规范,HIMEM.SYS程序符合XMS如果有兴趣更深入地了解 XMS规范,请打开本目录下的另一个文件o注意一下,本文是针对MS-DOS 7・10中自带的扩展内存驱动程序 HIMEM. SYS,不保证使用其它版本的或者其它扩展内存管理程序也能正确工作二驱动入口在使用驱动程序的功能之前,首先确认驱动程序是否存在,并找到功能的入口地 址DWORD xms.func; 〃保存XMS驱动程序的功能入口bool xms_ini t (){//首先确定XMS驱动程序是否存在BYTE isexist;asmmov AX, 0x4300int 0x2F回 80h,不变mov is exist, AL //如果已经装入XMS驱动,AL中会返} //否则,AL中的值if(is_exist != 0x80){printf(〃XMS not exist! “);return false;I//找到XMS功能入口点WORD segl, offl;asmmov AX, 0x4310int 0x2Fmov segl, ES //ES:BX就是入口点地址 mov off 1, BX//入口地址保存到xms_func中,下次当函数调用WORD* ptr_xms_func = (WORD*) (&xms_func); ptr xms func[0] = off1;ptr_xms_func[l]二 segl;return true;!三分配和释放使用扩展内存前要中请,用完之后要释放。
分配扩展内存‘length以1K字节为单位,返回分配内存的句柄:WORD xms_alloc(WORD lenth){WORD result, handle;asmmov DX, lengthmov AH, 9//功能号:9call xms_funcmov result, AXmov handle, DX//调用XMS驱动//AX二0,调用失败//DX二内存块句柄if (result == 0)return 0;elsereturn handle;}释放内存块:void xms free(WORD handle){asmmov DX, handiemov AH, 10call xms_func//已分配的内存块句柄//功能号:10//调用XMS驱动四读和写向扩展内存块中写数据或者从扩展内存块中读数据,都需要使用一个数据结构, 暂时把它命名为〃传送结构struct xms_trans{DWORD length; //要传送的字节数WORD source; //源内存块句柄,void far * source_addr; //源地址在内存块中的偏移量//如果source二0,表示在常规内存中,这吋source_addr是一个16:16的 指针WORD dest; //目的内存块句柄void far * dest_addr; //目的地址内存块中的偏移量//如果dest二0,表示在常规内存中,这时dest addr也是一个16:16的指 针};向扩展内存块中写数据:struct xms_trans trans 1; //作为全局变量,分配在数据段中bool xms write()WORD temp, result;transl. length = 66; //长度必须是偶数transl. source = 0; //源数据在常规内存中transl. source addr = (BYTE far *)pBuf; //源数据的地址transl.dest 二 DestHandle; 向这个内存块中写数据transl .dest_addr = 100;temp = FP OFF(&transl);asmmov AH, 11号:11mov ST, temptransl的地址call xms_func动〃目的地址在扩展内存中,//内存块中的偏移量//功能//DS:ST 是〃调用XMS驱//返回AX=O表mov result, AX示调用失败if (resuIt == 0)return false;elsereturn true;!从扩展内存中读数据:bool xms read (){WORD temp, result;transl. length = 66; //长度必须是偶数transl. source = Sourcellandle; //源数据在扩展内存中,从这个内存块中读数据transl. source_addr二100; //在内存块中的偏移量transl. dest = 0; //H的地址在常规内存中transl. dest addr = (BYTE far *)pBuf; //目的地址temp = FP_0FF(&transl);asmmov AH, 11//功能号:11//DS:ST 是//调用XMS驱//返回AX二0表mov ST, temptransl的地址cal 1 xms_func 动mov result, AX示调用失败if (result == 0)rcturn false;elsereturn true;五其他事项写程序的时候发现这样一个现象:void helloOprintf("hello everyone !〃);void main()printf(,,%04X:%04X\n,,,FP SEG (hello), FP OFF (hello)) ; //两次显示的值相同printf(〃%04X:%04X\n〃,FP_SEG(&hello), FP_0FF(&hello));while(!kbhit ());}使用FP_SEG和FP_OFF取得函数的地址,带与不带&符号,结果都一样。
肯定是 Borland C++把函数名仅仅当作一个标号来处理,标号就是段内偏移地址,对它 再用&取地址没有效果另外一个就是Borland C++中保存地址的变量都是4个字节,sizeof(int *)、 sizeof (void *)等于 4, sizeof (int far *)、sizeof (void far *)也等于 4也 就是说int far * pVal和int * pVal定义的效果是一样的,pVal高位两个字 节放段地址,低位两个字节放偏移地址下面这些实验代码对我们也有点意义:int x = 999;int far * px 二(int far *) (&x);int * pxx = (int *) (&x);〃相同printf (,z%d, %d \n", *px, *pxx);printf (,z%081X, %081X \n", px, pxx) ; //居然也相同 和段地址和偏移地址有关的宏和函数有FP_SEG:取地址4个字节中的高2个字节; FP OFF:取地址4个字节中的低2个字节; MK_FP:将两个2字节连接成一个4字节; peek, peekb:得到内存某个地址存放的数;poke, pokeb:向内存某个地址写数。
六总结上面写的就是扩展内存最基本的用法,其实这些对于我们已经足够,第四节读和 写两个函数是常规内存和扩展内存之间数据传输的最基木功能,各种各样的应用 在这两个函数基础上搭建的除此之外,XMS还有两个功能是把内存块锁定和解 锁,我们暂时不管它还有不要忘记了,分配的内存块要记得释放 在 此感谢dongsuoying提供的程序。