原创 DOS中使用扩展内存

2008-7-17 22:47 2131 2 2 分类: 工业电子

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提供的程序。

文章评论0条评论)

登录后参与讨论
相关推荐阅读
208servo_586623692 2013-11-17 16:56
基于Matlab的IIR数字滤波器设计方法比较及应用
  0 引言   数字滤波器(Digital Filter)是指输入、输出都是离散时间信号,通过一定运算关系改变输入信号所含频率成分的相对比例或者滤除某些频率成分的器件。数字滤波器在数字信号处理中起着...
208servo_586623692 2012-06-09 08:04
评论:@信号链杂谈's Blog 博客中提到的“信号链基础知识# 63:利用 SPICE 设计 TEC 温度环路 ...”
TEC温度环路PID控制...
208servo_586623692 2011-02-01 23:17
用DSP实现增量式光电编码器的细分
用DSP实现增量式光电编码器的细分 技术分类: 微处理器与DSP  | 2011-01-04 21ic   1 引言   目前,各类伺服驱动器及其应用中广泛采用光栅装置作为速度测量、位置测量的敏感元...
208servo_586623692 2011-02-01 23:16
基于MSP430F149单片机的光电编码器位置检测系统设计
基于MSP430F149单片机的光电编码器位置检测系统设计 技术分类: 测试与测量  | 2011-01-31 微计算机信息 李红果   在实际的工业位置控制领域中,为了提高控制精度,准确地对控制对...
208servo_586623692 2011-02-01 23:11
业界首款可直接安装在电机上的定制化电机控制模块
业界首款可直接安装在电机上的定制化电机控制模块 技术分类: 工业电子  | 2010-12-03 : EDN China    智能功率器件及解决方案供应商意法半导体发布业界首款整合工业级以太网接口...
208servo_586623692 2011-02-01 23:05
MEMS惯性传感器优势解析THELMA制程和低成本封装方法
MEMS惯性传感器优势解析:THELMA制程和低成本封装方法 技术分类: 模拟设计  | 2010-11-29 意法半导体 Benedetto Vigna   意法半导体公司推出一系列惯性传感器,极...
我要评论
0
2
1
2
3
4
5
6
7
8
9
0
关闭 热点推荐上一条 /4 下一条