原创 DOS中使用扩展内存

2008-7-17 22:47 2524 5 5 分类: 工业电子

DOS中使用扩展内存

下载地址http://www.blogjava.net/Files/wudiasm/DOS扩展内存.rar

DOS中使用扩展内存
一 HIMEM.SYS和XMS
扩展内存(extended memory)是指超过8086的1M限制的内存,在使用之前,必须有扩展内存驱动程序,MS-DOS 7.10操作系统带有这个程序。在config.sys文件中要加上一句话:DEVICE=C:\DOS71\HIMEM.SYS,然后就能在程序中访问扩展内存了。


XMS是扩展内存管理规范,HIMEM.SYS程序符合XMS。如果有兴趣更深入地了解XMS规范,请打开本目录下的另一个文件<eXtended Memory Specification (XMS), ver 3.0>。注意一下,本文是针对MS-DOS 7.10中自带的扩展内存驱动程序HIMEM.SYS,不保证使用其它版本的或者其它扩展内存管理程序也能正确工作。


二 驱动入口
在使用驱动程序的功能之前,首先确认驱动程序是否存在,并找到功能的入口地址。


DWORD xms_func;             //保存XMS驱动程序的功能入口


bool xms_init()
{
    //首先确定XMS驱动程序是否存在
    BYTE is_exist;
    __asm
    {
        mov AX, 0x4300
        int 0x2F
        mov is_exist, AL    //如果已经装入XMS驱动,AL中会返回80h,
    }                       //否则,AL中的值不变
    if(is_exist != 0x80)
    {
        printf("XMS not exist! ");
        return false;
    }


    //找到XMS功能入口点
    WORD seg1, off1;
    __asm
    {
        mov AX, 0x4310
        int 0x2F
        mov seg1, ES        //ES:BX就是入口点地址
        mov off1, BX
    }
   
    //入口地址保存到xms_func中,下次当函数调用
    WORD* ptr_xms_func = (WORD*)(&xms_func);
    ptr_xms_func[0] = off1;
    ptr_xms_func[1] = seg1;


    return true;
}


三 分配和释放
使用扩展内存前要申请,用完之后要释放。


分配扩展内存,length以1K字节为单位, 返回分配内存的句柄:
WORD xms_alloc(WORD lenth)
{
    WORD result, handle;


    __asm
    {
        mov DX, length
        mov AH, 9          //功能号:9
        call xms_func      //调用XMS驱动
        mov result, AX     //AX=0,调用失败
        mov handle, DX     //DX=内存块句柄
  }
  
    if(result == 0)
        return 0;
    else
        return handle;
}


释放内存块:
void xms_free(WORD handle)
{
    __asm
    {
        mov DX, handle     //已分配的内存块句柄
        mov AH, 10         //功能号:10
        call xms_func      //调用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 trans1;           //作为全局变量,分配在数据段中
bool xms_write()
{
    WORD temp, result;


    trans1.length = 66;             //长度必须是偶数
    trans1.source = 0;              //源数据在常规内存中
    trans1.source_addr = (BYTE far *)pBuf; //源数据的地址
    trans1.dest = DestHandle;       //目的地址在扩展内存中,向这个内存块中写数据
    trans1.dest_addr = 100;         //内存块中的偏移量


    temp = FP_OFF(&trans1);
    __asm
    {
        mov AH, 11                  //功能号:11
        mov SI, temp                //DS:SI是trans1的地址
        call xms_func               //调用XMS驱动
        mov result, AX              //返回AX=0表示调用失败
    }


    if(result == 0)
        return false;
    else
        return true;
}


从扩展内存中读数据:
bool xms_read()
{
    WORD temp, result;


    trans1.length = 66;            //长度必须是偶数
    trans1.source = SourceHandle;  //源数据在扩展内存中,从这个内存块中读数据
    trans1.source_addr = 100;      //在内存块中的偏移量
    trans1.dest = 0;               //目的地址在常规内存中
    trans1.dest_addr = (BYTE far *)pBuf; //目的地址


    temp = FP_OFF(&trans1);
    __asm
    {
        mov AH, 11                  //功能号:11
        mov SI, temp                //DS:SI是trans1的地址
        call xms_func               //调用XMS驱动
        mov result, AX              //返回AX=0表示调用失败
    }


    if(result == 0)
        return false;
    else
        return true;
}


五 其他事项
写程序的时候发现这样一个现象:
void hello()
{
    printf("hello everyone !");
}
void main()
{
    printf("%04X:%04X\n",FP_SEG(hello), FP_OFF(hello));    //两次显示的值相同
    printf("%04X:%04X\n", FP_SEG(&hello), FP_OFF(&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("%d, %d \n", *px, *pxx);      //相同
    printf("%08lX, %08lX \n", px, pxx);  //居然也相同


和段地址和偏移地址有关的宏和函数有
FP_SEG:取地址4个字节中的高2个字节;
FP_OFF:取地址4个字节中的低2个字节;
MK_FP:将两个2字节连接成一个4字节;
peek, peekb:得到内存某个地址存放的数;
poke,pokeb:向内存某个地址写数。


六 总结
上面写的就是扩展内存最基本的用法,其实这些对于我们已经足够,第四节读和写两个函数是常规内存和扩展内存之间数据传输的最基本功能,各种各样的应用在这两个函数基础上搭建的。除此之外,XMS还有两个功能是把内存块锁定和解锁,我们暂时不管它。还有不要忘记了,分配的内存块要记得释放。    在此感谢dongsuoying提供的程序。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
5
关闭 站长推荐上一条 /3 下一条