现在的位置: 首页 > 综合 > 正文

DOS程序员参考手册[10]

2013年09月14日 ⁄ 综合 ⁄ 共 36891字 ⁄ 字号 评论关闭

203页
        第四部分内存管理及其它
第10章程序和内存管理
    为了“使作业做完”,程序员已开发了许多装置和设备来从程序中获得内存所能包含
的更多信息。程序的连接、覆盖和其它技术长期以来一直是程序员技巧的主要成分。例如,
我们第一个使用的系统是只有32K内存的IBM 7040;有些程序没有覆盖技术就不能运
行(参见本节覆盖技术的讨论)。
    特殊化的技术能转变成调试时讨厌的东西,使程序高度地不可移植。因为大的系统可
以有增加的内存和处理机速度,所以计算机程序员已开发了一些技术来从其它程序内部
处理程序。命令外壳,如COMMAND.COM就采用这些技术来按需要执行程序。
    在一个DOS系统中,可以使用COMMAND.COM所使用的相同技术来从一个程序
的内部控制另一个程序的执行(这种能力与在UNIX系统中提供的性能相似)。这种操纵
程序功能的方式是相对清楚的。系统的重要模式能以单一标准的程序的形式存在。每个
这样的程序都能作为一个独立的实体而完整地测试和调试。UNIX程序员已经发现这项
技术对于程序开发是极为有效的。
    本章先介绍有多少内存可发挥作用,当需要时怎样获得更多内存以及怎样让DOS保
存不需要的内存。当在高级语言中工作时,编译程序管理着这个过程。而在汇编语言中,
就应该明确要对内存做些什么。本章还要看看扩展内存和扩充内存并检查一下能对它们
做什么。
    然后讨论程序执行——一个进程(父进程)怎样执行另一个进程(子进程)然后重新获
得控制。下面给出了一个简单的实例来说明这个过程的工作方式。
    最后解释一种特殊程序—TSR(终止和驻留)。TSR在许多方面与“通常的”程序
(应用程序如字处理程序和电子表格)不同。最明显的是TSR停止后,它们仍留在内存中,
并且不会被覆盖。TSR能以许多途径从另一个程序获得控制,其中最重要的是涉及一个
中断(见第11章“中断处理程序”)。本章将视野放在要遵循的程序上。
    然后,让我们继续看看内存管理。
                              〈程序覆盖〉
      程序覆盖是在内存中没有稳定地保持住的一部分代码或数据。该程序可能
    以许多二元图象的形式存在,其中每个图象都操纵一些特异的功能。主程序段操
纵了整个坐标,并且常常拥有覆盖所需的功能。
      在一个有覆盖的典型程序中,主菜单和普通功能在主覆盖块中,后者总是保

204页
存在内存中。无论程序何时需要一个子菜单,覆盖就安装在内存中的一个拥有覆
盖代码的区域内。然后主模式将控制传递给覆盖块,子菜单及其功能就能操作
了。
      用户结束了菜单,返回主菜单时,控制就传递回一级覆盖。因为子菜单的覆
盖在内存中不再需要,所以其内存空间能被另一个覆盖所使用(子菜单的程序代
码被新代码覆盖)。
                    10.1内存的工作方式
      基本的PC机或兼容机都有一个拥有1M内存的地址空间(记住一兆的字节是
1024K)。第3章“动态的DOS”指出该内存中只有640K能用于程序使用。剩下的384K分
配给ROM BIOS、显示适配器和盒式磁盘。
      但是较低的640K内存不只用于用户程序。其中,1024个字节的中断向量表由处理器
保存。只有中断向量在这些位置上,才能硬连线到处理器中(见第11章)。然后是BIOS和
DOS参数表、DOS内核、系统驱动程序,最后是命令处理程序的驻留部分。所有这些内存
都来自内存的用户区域。
      要计算失去的内存数量是很困难的,因为它要依据所使用的系统和巳安装的驱动程
序而定。安装的第一批用户程序(在内存图中)是TSR如Borland的SidekicK(10.1)是
已安装的TSR所使用的内存图示)。必须从有效空间中减去它们的内存使用。如果增加一
个与DESQview相似的窗口包,就只有大约350K内存可能从640K的起点上保存下来
——并且没有真正地启动程序去做什么!
      启动之后剩下的空间(通常用于安装Sidekick,大约500K到550K)是空着的,并且可
以使用。这个空间叫作暂用程序区域(TPA),这个名称也用在CP/M系统的等价区域上。
这个名称是合适的,因为用户程序在这个区域中暂驻——它们来或者去都是依据在系统
中工作的用户的需要而定的。
      PC机产生时,机器的内存640K的限制对于一个工作区域看起来是有限的。当时,具
备640K内存的PC是标准的;那些拥有128K内存及少数拥有256K内存的人是令人嫉
妒的。随着需求的增长,640K限制变成了一条非常有趣的笑话——它成了重要的系统限
制。Murphy法则的变化用于计算机内存:“不论你有多少,你总是需要更多的”。就成了一
个大笑话。
      有了80286芯片之后,PC系统就可能拥有16M的内存(有人说:“万事大吉”,其它人
则说:“现在我们有了一个更重要的系统”)。但DOS不能以相应的操作来利用这个内存。
超过1M限制的内存,已知是扩展内存,它对于80286处理器和后来的模型都是有用的,
DOS却不能使用它;要访问这种内存,80286芯片必须在保护方式中工作,DOS做不到这
一点。在DOS下面,只有特别编写的、能够打开CPU方式的程序才能使用扩展内存。

205页
    图10.1已安装的TSR所使用的内存
                                〈保护方式〉
        80286和80386处理器所具有的保护方式能访问控制多任务操作的特殊处
    理器功能。实地址方式提供8088/8086处理器只用来访问1M内存的同样的环
    境。将这个处理器移到保护方式中(通常需在一个操作系统中进行),该系统能控
    制多个程序在内存中的操作以及从一个任务到另一个任务的切换。
      本章不会详细介绍保护方式的性能,因为DOS不使用这种方式。要了解保
    护方式的更多信息,可查阅有关80286/80386 汇编语言程序设计方面的书籍。
    DOS在处理器的实地址方式中运行;于是1M以上的内存对于那些通过普通的DOS
功能来运行的程序来说是不可访问的。即使当一个系统有额外内存时,用户也不能有效地
使用它。
    尽管一些程序如DESQview能将它们的一些操作码插进使用80286扩展内存(像磁
盘一样)的RAM——磁盘驱动程序内,该内存对于大多数程序还是不能访问的(程序必
须在访问内存并完全返回实地址方式之前将处理器切换到保护方式)。 RAM磁盘在扩展
内存中操作时,它们也不必那么快,因为处理器从实地址方式切换到保护方式并返回来获

206页
得数据——这个过程可能相对较慢。在80286处理器上,换到硬盘可能更快一些。
      自从产生了个人计算机的AT型号以来,BIOS提供了两个功能来帮助程序确定有多
少内存是有用的(Int 15h,功能88h),然后将数据块移向或离开1M标记以上的扩展内存
(Int 15h,功能87M。这些例程的使用会带来严重的问题,因为没有对扩展的内存的空间
进行管理。一个程序能很容易地在另一个程序(或一个RAM——磁盘驱动程序)保存在
扩展内存空间中的数据上进行写操作;没有什么能说明这个问题或报告这个错误。
      扩充内存的第一次出现,是作为一个联合工作组提出的。该工作组由Louts和Intel
在1985年春季的COMDEX展示会介绍给人们的(3.0版本),它提供了一条途径,允许访
问8M那么大的内存,而不需要在处理器方式中进行特别的移动。扩充内存使处理器能通
过16K页来访问额外的内存,该页能映射进640K与1M之间的内存空间里未用的区域
之中。4个16K页映射进64K框架,其位置由安装板时用户的系统决定(见第3章“动态
的DOS”,那里深入地讨论了扩充内存)。
      扩充内存管理(EMM)使扩充内存活动起来像一个带有句柄的文件。当程序在扩充内
存中请求空间时,EMM就将这个空间放在一边,并返回能用来获得对该空间进行访问的
一个独一无二的“句柄”。
      要想从扩充内存中获得16K页,可以使用Int 67h来调用EMM,并告诉它将该页放
进页框架之中。然后就能直接从程序中定位此内存。图10.2显示了所发生的事情。
                           图10.2扩充内存的访问
      将下列任意一行加到CONFIG.SYS文件中就能安装EMM.SYS(扩充内存管理器
驱动程序):
          DRIVER=EMM.SYS
          DRIVER=EMM386.SYS
      这些驱动程序运行起来部分像一个真实的驱动程序(真实的驱动程序在第12章“设
备驱动程序”中介绍;目前,就先认为扩充内存管理器驱动程序不能正常工作)。不能像对
待其它驱动程序那样去访问EMM.SYS或EMM386.SYS,而是通过Int 67h来访问它的

207页
功能。可参看“DOS参考手册”一节中有关Int 67h各种功能的详细列表。
    EMM.SYS包括以下这些有用功能:
    ·报告扩充内存的状态
    ·在扩充内存中分配页
    ·在扩充内存中释放分配页
      ·诊断
      ·多任务支持
    ·将物理页映到扩充内存中,变成分配给程序的逻辑页。
    这种内存分页技术长期以来用于计算机中。当有扩充内存可用时,没有理由不去利用
它。
    扩充内存产生后不久,Microsoft宣布它支持该标准的3.2版本,其中包括了对于多
任务操作系统有用的设备。3.2版本变成了LIM EMS(Lotus-Intel-Microsoft扩充内存规
范)。Ashton-Tate, Ast Research和Quandram注意到这种标准的局限性:只能将一个16K
页映射到内存的一个未使用区域之中。那么为什么不将来自TPA的内存的大区域映射
进去呢?这就引导人们开发了增强的扩充内存规范(EEMS)。
    用增强的扩充内存就能将一个完整的程序移到扩充内存区域中并代替另一个程序。
这种功能意味着一台PC机能变成多任务的系统。1988年产生的LIM4.0,除了包含旧的
LIM 3.2以外,还包括EEMS。所有涉及扩充内存技术的公司都在一定程度上支持这个新
的LIM,尽管并非所有的公司都执行了该标准中要求的所有功能。
    只要我们使用基本的PC机,就必须学会与分配给我们的640K的空间区域一起生
活。当然,新的机器型号突破了这个限制。
                    10.2内存管理
    DOS之下的内存管理涉及到TPA中的自由区域。要保持程序和未来出现的操作系
统之间的兼容性,就应该将DOS调用用于所有的内存分配和释放的请求之中,
    尽管当前我们能在内存中使用各种技巧,但当移向多任务系统时,这些使用了特殊技
巧的程序会停止运行。了解了在DOS局限性下工作,就能编写出更便利的程序。遗憾的
是,一些技巧(如直接访问视频显示内存)是DOS编程的主要成分。没有它们,系统不会敏
感到能给用户提供专业程序所必需的“感觉”类型。尽管这些花招使程序没有那么方便,但
有时要编写好程序,失去一点便利性也是必要的。
    而在内存分配中却没有这些技巧。甚至一个小的错误也会使该系统死锁。让我们先
看看DOS控制TPA中内存的方式。
    TPA安排在一个叫做“内存场(memory arena)”的结构之中。 DOS维护着这个被称为
场项的内存块的链表,每一项都有它自己的特殊控制块,叫做“场头(arena header)”(第3
章讨论这些题目,表3.3显示了场头的布局)。3个DOS功能(Int 29h,功能48h、49h和
4Ah)用于请求或释放内存:“DOS参考手册”一节将详细讨论每个功能。

208页
 场链(见图10.3)将每个内存块链接成内存块表。不论两个空白内存块何时碰到了一
起,它们都结合成单一的较大的,且在链中只有一个场头的块。
                                    图10.3内存分配链
    请求内存时,DOS搜索内存场链来分配一个能满足请求的块。要分配这些块,DOS采
用了最先适配策略,其中它利用了第一个够大的能满足需要的块。如果该块所含内存超过
了需要,就将它分成两部分,多余的内存作为一个独立内存块放回到链表中。
    DOS之所以使用这个策略,是因为它是通常使用中最有效的。从DOS 3.0版开始,可
将分配策略变成下列替代方法之一:
      ·最佳适配在这个策略中,要搜索整个内存并且用与要求最紧密匹配的内存块来
          填满它。
      ·最后适配使用链中能满足分配需要的最后一个块(该块带有最高的内存地址)。
    内存分配策略不需改变,因为最有效的方法已经在使用(用DOS 3.0版进行的非常
有限的测试,看起来是指示DOS在它安装任何程序时都继续使用最先适配策略,也不
管设置了哪种方法。这个结果并不是最后的——因为随着所涉及的变化因素的增多,就需
要更多的测试来提供有限的回答。一个问题是DOS的装入程序开始时,总是要试着获得
所有保留的RAM)。
    无论DOS何时得到一个分配请求,它都会检查内存场链表,看看是否存在什么问题。
如果在写内存时,冲掉了这个场头,或者损坏了这个表,那么程序就会废弃并且看到这样
一条信息:Memory Allocation Error(内存分配出错)。

209页
10.2.1压缩程序内存
    当COM程序启动时,系统会将所有内存都分配给它,因为DOS生来就是一个单用
户的操作系统,每次只能运行一个程序。装入程序一开始就请求65,535个段(比可能得到
的要多)来找到有多少可用的段,然后它再要求精确的数目。
    EXE程序也分配了所有内存,但在这里如果愿意,就可以减少所分配的内存。EXE程
序的程序头有2个参数:MINALLOC和MAXALLOC。前者是程序运行所需的最小的内
存分配。如果一个块有这个最小数量的内存,那么这个块有效时,该程序就能运行。
    但DOS总是在可能的情况下,努力将一个带有MAXALLOC内存字节的块分配给
程序。 Microsoft的连接程序,总是设置MINALLOC为0,MAXALLOC为FFFFh(1048560
个字节),除非用户告诉它使用别的值。无论何时,在没有特异化这些值的情况下连结一个
EXE程序,都要保证它获得所有有效的内存。
    注意Turbo C和Microsoft C都在启动时自动释放多余的内存。但Turbo Pascal像
Microsoft的连接程序一样不能自动地完成这件事。在Turbo Pascal 4.0版和5.0版中(它
们能产生EXE文件),所有有效内存都被默认值所使用,将最高区域管理成一个堆。但是
如果想自己控制它,有一个编译程序指令,就能设置大小。另外,内部变量(在手册中已清
楚地公开了)精确告知使用内存的方式。
    旧的Turbo Pascal版本能产生COM文件,这些版本之所以能限制程序所用内存数量
的唯一方式,是因为在编译过程中为堆设置一个最大值的容量。由于内存分配的方式,以
及程序之上的堆栈和堆,所以不存在有效的方法来确定Pascal COM程序结束的地方。
    BASIC也向程序员提出了这一问题,因为没有有效的方式来确定程序在内存中的结
束地址,也没有办法启动一个程序,除非访问EXE功能,特殊的MEMSET功能让程序设
置内存上限,但该功能有意允许BASIC程序安装汇编语言支持例程而不是提供其它程序
的执行。CHAIN和RUN(BASIC语句用来执行其它BASIC程序)用一个新程序代替业巳
存在的程序,而不是保持当前程序的状态。
    在高级语言中,C和Turbo Pascal的较新版本都适于动态的内存分配工作。C给内存
分配和释放提供了较好的功能;从来不必为此目的而访问DOS功能。最近的Turbo Pascal
版本综合了传统(或标准)Pascal的许多扩展名,并以略有不同的名字提供了像C一样的
功能。
    只有以汇编语言编写的程序必须明确地将内存在启动处返回到内存池中。列表10.1
显示了这样做的方法。
    列表10.1
;Freemem.asm
            mov sp,offset stack          ; Move stack to safe area
            mov ah,4Ah                   ; Setblock
            mov bx,280h                   ;Retain 10K of space
            int 21h
            jc    alloc_error            ; Allocation error
210页
;            [MORE PROGRAM CODE]
      dw 64 dup(?)
     stack  equ $
10.2.2获得更多的内存
      如果程序需要额外的内存,它可以用修改内存分配功能(Int 21h,功能4Ah)来请求
额外的内存。作为回报,如果内存是可用的,进位标志会清零,AX寄存器含有内存基的段
地址,如果没有足够的内存来满足请求,就会设置进位标志,AX含有一个出错标志(7=
控制块破坏,8=内存不够),寄存器BX拥有最大有效块的大小。
      C和Turbo Pascal的较新版本都提供有用的函数以获得额外的内存并释放内存。旧
    的Turbo Pascal版本(V4之前的)请求另外内存的能力最小或不存在,除非使用特殊的编
译程序开关。BASIC未提供方法来改变给BASIC程序的内存分配,也没有提供方法来确
    定从哪里进入内存,去限制内存使用。
      只有在汇编语言中才必须直接访问DOS分配功能。在C和TurboPascal的较新版本
    中,由库分配例程去访问DOS功能。列表10.2提供了一种方法来获得内存,并确定如果
    禁止第一次尝试,将留下多少内存。
      列表10.2
            ;Getmem.asm
            getmem: mov         ah,48h                    ;Allocate memory
                      mov       bx,bufsize               ;16K memory
                      int      21h
                      jc         nomem                    ;Cannot allocate
                      mov        bufSeg,ax                ;Save pointer
                      jmp        pgm                       ;Continue program
            nomem :   Cmp       aX,8
                      jnz        quit                      ;Major alloc error
                      mov        bufsize,bx                ;Save buffer size
                      mov        ah,48h                  ;Allocate memory
                      int      21h
                jC    quit                 ;Still cannot allocate
              pgm:
          ;                 [MAIN PART OF PROGRAM]
          done:     mov     ah,49h                ;De·allocate memory
              mov      es,bufseg     ;Point to buffer segment
                          int        21h
          quit :            [EXIT CODE GOES HERE]
          bufsize dW      400h
          bufseg dw          0
      在没有访问DOS有关内存分配的请求的情况下,许多内存分配和释放的功能对于用
C或Turbo Pascal的较新的版本编写的程序都是有用的。使用这些有效的功能很有意思,
因为它们可明显简化操作,并将它保持在语言系统的控制之下。Turbo Pascal V4.0以前
的版本在堆中拥有所有可用的内存,并且提供了程序空间的分配和释放功能。这些Pascal

211页
分配和释放例程不会从内存场中获得或返回空间。BASIC不请求也不返回什么。
    使用BASIC和Pascal(Turbo Pascal 4.0之前)的程序员能用编译程序开关来人工限
制内存,然后用DOS功能调用来请求释放另外的内存。但这个练习会带来混乱和内存处
理不力。必须有动态内存处理的程序,应使用C来编写,或用新的Turbo Pascal,或者干脆
用汇编语言来编写。
                    10.3扩充内存
    或迟或早(当在PC上用完内存并已添加所有必要的芯片来使程序达到640K上限
时),用户可能要购买一块扩充内存板。有了扩充内存,就能对大量数据存储(EMS)或多
任务区域(EEMS)进行快速而有效的访问。
10.3.1确定扩充内存的有效性
    要确定是否已安装了扩充内存,可使用下列方法之一:
    ·试着打开文件EMMXXXX0(设备驱动程序的推荐名)。如果能成功能地打开,那
      么就有驱动程序存在或存在有相同名字的文件。要看看是否有驱动程序,可用
      IOCTL功能来给出一个“获得输出状态”的请求。驱动程序返回FFh;文件就返回
      00h。关闭该文件,以便重新使用这个句柄。
    ·考察在Int 67h向量位置的地址。该地址是驱动程序的中断入口点。如果有EMM.
      SYS(或一个可替代的驱动程序),所给的段地址就是该驱动程序的基地址;该段
      地址中的偏移值00Ah处,驱动程序名作为驱动程序头的一部分而出现。尽管该
      过程比打开文件的过程(打开和关闭的开销是显著的)要快,该方法要依赖程序去
      访问其正常内存范围以外的内存。
    列表10.3和列表10.4给出了两个用C编写的独立的例程,检查EMS驱动程序是
否存在。
列表10.3
        /*emmtest.c
        Listing 10.3 of DOS Programmer's Reference*/
      #include <dos.h>
      int emmtest()
      {
          union REGS regs;
          struct SREGS sregs;
          short int result;
          unsigned int handle;
          regs.h.ah=0x3d;           /*Open file*/
          regs.h.al=0;               /*Read mode only*/
          regs.X.dX=FP_OFF("EMMxxxX0");/*File name*/
          sregS.dS=FP_SEG("EMMXXxx0");/*Set the DS register*/
          intdosx(&regs,&regs,&Sregs);
          handle=regs.x.ax;          /*File handle*/
212页
              /*If opened OK,then close the file*/
              if(result=(regs.x.cflag==0)){
                    regs.h.ah=0x3e;
                    regs.x.bX=handle;
                    intdoS(&regs,&regs);
              }
             return (result);
        }
    列表10.3用打开文件的方法去确定EMM是否已安装,它不会去检查IOCTL调用,
看看该方法返回了一个文件或是一个驱动程序。大多数情况下,这不是一个问题。但为了
安全起见,应该向该功能增加一个IOCTL调用的检查。
    注意:用Borland C++编译该函数,要在if()语句中产生一个警告,该语句设置re-
sult。这个警告能安全地忽略,因为所期望的效果是设置功能返回值为TRUE或FALSE,
根据测试结果而定,编译程序虽抱怨它但仍能处理它。
    列表10.4对于某些不习惯涉及远(far)指针的C程序员,它变成了一个真正的问题。
当在PC机上工作时,必须意识到各种指针之间的区别:在一个段内进行指向操作的指针
(near指针);能够指向内存、所给定的段和偏移值地址的任何地方的指针(far指针);以及
能指向操作内存任何位置的指针,就像内存没有划分成段一样(huge指针)。不论何时使
用指针变量,都必须谨慎并且应一直保持与它们之间的匹配;否则意外的结果会阻止程序
运行。
    列表10.4
          /*emmchk.c
          LiSting 10.4 of DOS Programmer's Reference*/
        #define FALSE 0
        #define TRUE    !FALSE
          #include<Stdlib.h>
          #include<Stdio.h>
          #include<dos.h>
          #include<alloc.h>
          int emmchk()
          {
            union REGS regs;
            struct SREGS sregs;
            char far*emptr,far*nameptr,far*fmptr;
            nameptr="EMMxxxx0";
            regs.h.ah=0x35;           /*Get Interrupt vector*/
            regs.h.al=0x67;              /* Get it for the EMM */
            intdosx(&regs,&regs,&sregs);
            /*Make a FAR pointer to access the driver*/
            emptr=MK_FP(sregs.es,0);
            fmptr=farmalloc(sregs.es);
            if((emptr=fmptr)==NULL{
                  printf("Unable to allocate far pointer.\n");
                  exit(0);
            }
213页
        /*Return TRUE if they are the same for eight CharacterS*/
        return (farcmp(emptr+10,nameptr,8));
}
int farcmp(Str1,str2,n)
       char far*str1,
            far*str2;
        int n;
{
        while(*str2 && n>0){
              if(*str1!=*str2)return(FALSE);
                  n--;
                  str1++;Str2++;
          }
              return (TRUE);
    }
    一些书籍介绍扩充内存的方式会产生一个更为严重的错误。因为许多扩充内存方面
的讨论都错误地解释说Int 67h处的指针指向驱动程序的开始,并且所给的在Int 67h向
量上的内存地址的10个字节是名字EMMXXX0。马上就会知道这种说法不会是真的;中
断向量在被调用时会指向控制传递的位置,但驱动程序的起始字节是驱动程序头的一部
分,它不能是可执行的代码(这个问题将在第12章里讨论)。
     该向量提供了地址的段部分,但在段内部,在绝对偏移值000Ah处能找到这个名字,
因为驱动程序的内存总是在段边界上对齐(只能分配多个完整的段)。任何已装入的驱动
程序基地址的段地址都要规范化——即段地址要调整,直到偏移值为0。
     列表10.5是一个用两种方法来检验扩充内存是否存在的小程序,它还显示每种方法
返回的内容。                                      
 列表10.5
/*testemm.C
      Listing 10.5 of DOS programmer'S Reference*/
#include<Stdio.h>
VOid main()

          int emmchk(void);
          int emmteSt(void);
          if(emmchk())
                printf("MEM:Expanded memory is present\n");
          else
                printf("MEM:Expanded memory is NOT present\n");
          if(emmteSt())
                printf("OPEN:Expanded memory is present\n");
          else
                printf("OPEN:Expanded memory is NOT present\n");
}
10.3.2使用扩充内存
    知道有扩充内存可用时,就可以使用扩充内存功能(与Int 67h紧密相关)来获得并使
                                                                                         
214页
用内存(附录部分的EMS一节将详细介绍扩充内存的功能)。列表10.6给出了一个使用
Int 67h的内存功能的一个简单实例。
列表10.6
/*emstest.c
Listing 10.6 of DOS Programmer's Reference*/
#include<stdio.h>
#include<Stdlib.h>
#include<string.h>
#include<dos.h>
#define FALSE 0
#define TRUE !FALSE
#define EMM 0X67
Char far*emmbase;
void main()
{
unsigned int emmhandle;
char teststr[80];
int i;
int emmtest(void);
int emmok(void);
unsigned int emmalloc(int n);
unsigned int emmmap(unsigned int handle,int phys,int page);
void emmmove(int page,char*str,int n);
void emmget(int page,char*str,int n);
unsigned int emmclose(unsigned int handle);
/*Is there any expanded memory?*/
if(!emmtest()){
printf("Expanded memory is NOT present/n");
printf("Cannot run this program/n");
exit(0);
}
 /*Is the expanded memory manager functional?*/
if(!emmok()){
printf("Expanded memory manager NOT available/n");
printf("cannOt run this program/n");
exit(0);
}
/*Get ten pages of expanded memory for the demo.*/
if((emmhandle=emmalloc(10))<0){
printf("There are not enough pages available/n");
printf("cannot run this program/n");
exit(0);
}
/*Write the test string into each of the ten pages.*/
for(i=0;i<10;i++){
Sprintf(teststr,"This info is in EMS page %d/n",i);
emmmap(emmhandle,i,0);
emmmove(0,teststr,strlen(teststr)+1);
}

215页
/*Now read them back in and recover the test string.*/
fOr(i=0;i<10;i++){
 emmmap(emmhandle,i,0);
emmget(0,teststr,strlen(teststr)+1);
printf("Reading from block %d:%s",i,teStstr);
}
/*Finally,release the expanded memory*/
emmclose(emmhandle);
}
int emmtest()
{
union REGS regs;
 struct SREGS sregs;
short int result;
unSigned int handle;
regs.h.ah=0x3d;/*open file*/
regS.h.al=0;/*Read mOde Only*/
regS.x.dx=(int)"EMMXXXX0"; /*File name*/
sregS.ds=_DS; /*Set the DS register*/
intdosx(&regs,&regs,&sregs);
handle=regS.x.ax;/*File handle*/
if(result=(regs.x.cflag==0)){
regs.h.ah=0x3e;
regs.x.bx=handle;
intdos(&regs,&regs);
}
return (result);
}
int emmok()
{
union REGS regs;
regs.h.ah=0x46;/*Get manager status*/
int86(EMM,&regS,&regs);
if(regs.h.ah!=0)return (FALSE);
regs.h.ah=0x41;/*Get page frame segment*/
int86(EMM,&regS,&regs);
if(regs.h.ah!=0)return (FALSE);
emmbase=MK_FP(regs.x.bx,0);
return (TRUE);
}
unsigned int emmalloc(n)
int n;
{
union REGS regs;
regs.h.ah=0x43; /*Get handle and allocate memory*/
regs.x.bx=n;
int86(EMM,&regs,&regs);
if(regs.h.ah!=0)
return (-1);
return (regs.x.dx);
}
unsigned int emmmap(handle,phys,page)
unsigned int handle;

216页
              int phys,page;
          {
              union REGS regs;
              regS.h.ah=0x44;        /*Map memory*/
              regs.h.al=page;
              regS.x.bX=phys;
              regS.x.dX=handle;
              int86(EMM,&regS,&regs);
              return (regS.h.ah==0);
          }
          vOid emmmOve(page,str,n)
              int page,n;
              Char *str;
          {
              char far*ptr;
              ptr=emmbaSe+page*16384;
              while(n-->0)
                    *ptr++=*Str++;
          }
          void emmget(page,str,n)
              int page,n;
              char*str;
          {
              char far*ptr;
              ptr=emmbase+page*16384;
              while(n-->0)
                    *Str++=*ptr++;
          }
          unSigned int emmclose(handle)
              UnSigned int       handle;
          {    
            union REGS regs;
              regs.h.ah=0x45;         /*Release handle*/
              regs.x.dX=handle;
              int86(EMM,&regS,&regs);
              return (regs.h.ah==0);
          }
      该程序分析了扩充内存的基本操作。如下所示:
      ·检验扩充内存是否存在。
      ·然后看看扩充内存管理程序是否在正常工作。
      ·试着分配10页扩充内存。
      ·每次将一页映射到页框架中,并将一个测试字符串写进每一页中。
      ·将这些页映射回页框架中,然后读出并打印测试字符串。
      这些是扩充内存上的最基本操作。它们使你能在多个程序中利用内存区。可利用这
类技术将电子表格或数据库放进扩充内存:要作到这一点,就要在页框架中限定一个值数
组,并用它们的指针填满它们。记住:要用far指针或大指针来访问页,因为这个区域将超
出当前段。

217页
    程序的主要部分后面是主程序所使用的许多小的扩充内存功能。将这些功能组合起
来这些功能便覆盖了扩充内存的操作,尽管还可能想向它们添加错误检查。
    程序所包含的函数有:
    emmtest   检验内存中是否存在扩充内存管理程序。
    emmok     检验扩充内存管理程序的功能并确定页框架的基地址。
    emmalloc  从扩充内存管理程序请求指定数量的页数。
    emmmap    将一个扩充内存页映射进页框架中。
    emmmove   把数据移进页框架中的指定页中。
    emmget    从页框架中的指定页中获得数据。
    emmclose  将扩充内存句柄的控制返回给扩充内存管理程序。
    扩充内存的简单应用程序能用这些基本功能来启动,然后适当地增加错误检查和新
的功能,就可以发展成羽翼丰满的程序。
                    10.4扩展内存
    扩展内存是指超过8086的1M限制的内存。在扩展内存规范(XMS)的范围内,扩展
内存还指高位内存区(HMA)和上位内存块(UMB)。
    UMB是DOS640K内存界线与8086 1M界线之间的有用内存区域。它们通常不与
640K有用内存相连接,并且在DOS5.0之前,它们只有通过XMS驱动程序才对程序员
有用。从DOS5.0开始,UMB能通过DOS的内存服务来访问;DOS内存服务则可为你访
问XMS驱动程序。
    HMA是一个奇怪的东西,在其中当CPU位于实地址方式中时,第21个地址线(A20
线)被激活,增加65520个字节,增加可用内存的数量,所增加的内存在实地址方式中的
CPU也能直接访问它。这个过程的工作方式如下:考察一下段值加上偏移值对物理内存
的映射,就会发现段值乘以16再加偏移值,就是所访问的物理地址。如果这个值超过20
位,那么就丢掉较高的几位,将物理地址映射在000000H到0FFFFFH的范围内。于是,地
址0FFFF:0010H在A20为0时,映射的物理地址是000000H,在A20为1时,映射的物
理地址是0100000H。额外的65520个内存字节正是来自这里;从0FFFF : 0010H到
0FFFF : FFFFH这些地址通常映射到物理地址000000H到00FFEFH上,而在这里,它
们映射到了物理地址010000H到010FFEFH上。
    XMS驱动程序提供了5组服务:驱动程序信息功能;HMA管理功能;A20管理功
能;扩展内存管理功能以及上位内存管理功能。两个另外的服务确定XMS驱动程序是否
存在并且获得XMS驱动程序的控制功能的地址。
10.4.1确定扩展内存的有效性
    要确定XMS驱动程序是否存在,可以执行下列代码:
      mov AX,04300H
                                                                                                   
218页
        int   02FH                                                                                                ·=
      cmp AL, 080H                                      =                      ::
      jne XMS_NotPresent
      ;XMS is present
    如果XMS存在,就需要获得XMS驱动程序的控制功能的地址。这可借助下列代码
片段来完成:
      code segment
      mov AX,04310H                                              ”
      int 02FH
      mov word ptr[XMS_Control],BX
      mov word ptr[XMS_COntrol],ES
      cOde ends
      data segment
      XMS_Control    dd      (?)
      data ends
10.4.2使用扩展内存
    如果扩展内存可用,就可以用扩展内存功能来获得和使用扩展内存(XMS参考手册
一节位于本书的第5部分,那里介绍了扩展内存功能)。本节中的两个列表是扩展内存功
能方面的简单实例。
    列表10.7中的程序分析了扩展内存的基本操作。如下所示:   
      ·检验一下扩展内存是否存在。
      ·获得XMS版本号,检查一下HMA是否存在,并获得扩展内存最大空白块的大
        小。
      ·试着分配一个扩展内存块。
      ·向扩展内存块写数据。
      ·从扩展内存块读回数据。
      ·释放扩展内存块。
      ·试着分配一个上位内存块。
      ·释放上位内存块。
      这是在扩展内存中的基本操作。它们使你能在多个程序中运行内存区。可用这类技
术将数据放入扩展内存中。  
    列表10.7
          /*xmstest.C
          Listing 10.7 of DOS Programmer's Reference*/
        #include<ctype.h>
          #include<dos.h>

219页
#include<stdiO.h>
#include<mem.h>
Struct XMS_Status{
unsigned short version;
unsigned short revision;
 unsigned short Hma_existence;
};
Struct XMS_memory_StatuS{
unsigned short largest_block;
unSigned ShOrt tOtal_free_memOry;
unsigned short error_status;
};
struct bstat{
unSigned Short lock_count;
unSigned shOrt free_handle_count;
};
union memory_address{
unSigned long  long_offset;
struct conventional_address{
unsigned short conventional_offset;
unsigned short conventional_segment;
} C_a;
};
Struct XMS_move_data{
 unsigned long transfer_size;
unsigned short source_handle;
UniOn memOry_address sOurce_OffSet;
unsigned short deStination_handle;
union memory_addreSS deStination_offset;
};
Struct umb_data{
unsigned short segment_number;
unsigned short segment_size;
};
extern unsigned short far
a20_stat(void);
extern unsigned short far
alter_a20(unSigned Short change_code);
extern unsigned short far
hma_alloc(unsigned ShOrt size_in_byteS);
extern unsigned short far
hma_free(void);
extern void far
hma_move(unsigned short count, unsigned short direction,
unsigned short hma_offset, vOid far*data);
extern unsigned short far
umb_alloc(unsigned short size struct umb data far*ptr);
extern unsigned short far
umb_free(unsigned short segment);
extern unsigned short far
xms_alloc(unsigned short size,unsigned short far*handle);
extern void far
xms_avail(struct XMS_memOry_StatuS far*ptr);
extern unsigned short far
xms_bstat(unsigned short handle, struct bstat far*ptr);
extern unsigned short far

220页
xms_free(unsigned short handle);
extern unsigned short far
xms_lock(unsigned short handle, unsigned long *linear_address);
extern unsigned short far
xms_move(Struct XMS_move_data far *ptr);
extern unsigned short far
xms_realloc(unsigned short handle, unsigned short size);
extern void far
xms_stat(struct XMS_status far *ptr);
extern unsigned short far
xms_test(void);
extern unsigned short far
xms_unlock(unsigned short handle);
/* Return Codes */
#define A20_DISABLED (0*0000)
#define A20_ENABLED (0*0001)
#define FAILURE (0*0000)
#define SUCCESS (0*0001)
#define FUNCTION_NOT_IMPLEMENTED (0*0080)
#define VDISK_DEVICE_DETECTED (0*0081)
#define A20_ERROR_OCCURRED (0*0082)
#define HMA_DOES_NOT_EXIST (0*0090)
#define HMA_ALREADY_IN_USE (0*0091)
#define NOT_ENOUGH_BYTES_TO_ALLOC_HMA (0*0092)
#define HMA_NOT_ALLOCATED (0*0093)
#define A20_LINE_STILL_ENABLED (0*0094)
#define ALL_EXTENDED_MEMORY_ALLOCATED (0*00A0)
#define ALL_EXTENDED_MEMORY_HANDLES_USED (0*00A1)
#define INVALID_HANDLE (0*00A2)
#define INVALID_SOURCE_HANDLE (0*00A3)
#define INVALID_SOURCE_OFFSET (0*00A4)
#define INVALID_DESTINATION_HANDLE (0*00A5)
#define INVALID_DESTINATION_OFFSET (0*00A6)
#define INVALID_LENGTH (0*00A7)
#define INVALID_OVERLAP (0*00A8)
#define PARITY_ERROR (0*00A9)
#define BLOCK_NOT_LOCKED (0*00AA)
#define HANDLE_IS_LOCKED (0*00AB)
#define LOCK_COUNT_OVERFLOW (0*00AC)
#define LOCK_FAILED (0*00AD)
#define SMALLER_UMBAVAILABLE (0*00B0)
#define NO_UMBS_AVAILABLE (0*00B1)
#define INVALID_UMB_SEGMENT  (0*00B2)
#define ILLEGAL_PARAMETER (0*FFFF)
/* Codes for alter_a20() */
#define GLOBAL_A20_LINE (0)
#define LOCAL_A20_LINE (2)
#define ENABLE_A20_LINE (0)
#define DISABLE_A20_LINE (1)
/* Codes for hma_move() */
#define COPY_FROM_HMA (1)
#define COPY_TO_HMA (0)
#define KILOBYTE (1024)
unsigned char test_buffer[KILOBYTE];
void test_hma(void)

221页
{
int HMA_allocated;
int A20_status;
int original_a20_status;
unsigned short result;
HMA_allocated=0;
result = hma_alloc(0*FFF0);
switch(result)
{
case SUCCESS:
printf("HMA successfully allocated to current process/n");
HMA_allocated++;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("HMA allocate not implemented by XMS/n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't allocate HMA/n");
break;
Case HMA_DOES_NOT_EXIST:
printf("HMA cannot be allocated; doesn't exist/n");
return;
case HMA_ALREADY_IN_USE:
printf("HMA cannot be allocated; already in use/n");
break;
case NOT_ENOUGH_BYTES_TO_ALLOC_HMA:
printf("Not enough bytes to allocate HMA were specified/n");
return;
default:
printf("Invalid result of hma_alloc(): 0*%.4X/n", result);
return;
}
if(HMA_allocated)
{
Original_A20_Status = A20_StatuS = -1;
result = a20_stat();
switch(result)
{
case A20_DISABLED:
original_A20_Status=A20_Status = 0;
break;
case A20 ENABLED:
Original_A20_Status = A20_Status = 1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Can't get A20 line status; function not implemented/n");
break;
case VDISK_DEVICE_DETECTED:
printf("Can't get A20 line Status; VDISK device detected/n");
break;
default:
printf("Can't get A20 line Status; unexpected result 0*%.4X/n",
result);
break;
}
if(A20_status == 0) /* Disabled */
{
result = alter_a20(GLOBAL_A20_LINE|ENABLE_A20_LINE);
switch(result)
{
case SUCCESS:

222页
printf("A20 line globally enabled/n");
A20_status=1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented; couldn't enable A20 line/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't enable A20 line/n");
break;
case A20_ERROR_OCCURRED:
printf("Error occurred while attempting to enable A20 line/n");
break;
case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()/n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()/n", result);
break;
}
}
if(A20_status==1)
{
int k;
memset(test_buffer,0*55,KILOBYTE);
hma_move(KILOBYTE,Copy_to_HMA,0,test_buffer);
memset(test_buffer, 0*AA, KILOBYTE);
hma_move(KILOBYTE,copy_FROM_HMA,0,test_buffer);
result=1;
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*55)
{
printf("Error transferring data to/from HMA/n");
result=0;
break;
}
}
if(result)
{
memset(test_buffer, 0*AA, KILOBYTE);
hma_move(KILOBYTE,COPY_TO_HMA,0,test_buffer);
memset(test_buffer,0*55, KILOBYTE);
hma_move(KILOBYTE, COPY_FROM_HMA,0,test_buffer);
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*AA)
{
printf("Error transferring data to/from HMA/n");
result=0;
break;
}
}
}
if(result)
printf("Transfer of data to/from HMA test successful/n");
if(original_A20_status==0)
{
result=alter_a20(GLOBAL_A20_LiNE|DISABLE_A20_LINE);
switch(result)
{
case SUCCESS:

223页
printf("A20 line glObally disabled/n");
break;
 Case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented, disable A20 line/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; disable A20 line/n");
break;
Case A20_ERROR_OCCURRED:
printf("Error while attempting to disable  A20 line/n");
break;
Case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()/n");
 break;
case A20_LINE_STILL_ENABLED:
printf("A20 line still enabled/n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()/n ",
result);
break;
}
}
}
result=hma_free();
switch(result)
{
Case SUCCESS:
printf("HMA successfully freed/ n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("HMA free not implemented by XMS/n");
. break; .
case VDISK_DEViCE_DETECTED:
printf("VDISK device detected; couldn't free HMA/n");
break;
Case HMA_DOES_NOT_EXIST:
printf("HMA does not exist/n");
 return;
case HMA_NOT_ALLOCATED:
printf("HMA was not allocated/n");
return;
default:
printf("Invalid result of hma_fRee(): 0*%.4X/n", result);
return;
}
}
else
 {
Original_A20_status=A20_status=-1;
result=a20_stat();
switch(result)
{
case A20_DISABLED:
original_A20_status = A20_status = 0;
break;
case A20_ENABLED:
original_A20_status=A20_status=1;
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf("Can't get A20 line status; function not implemented/n");
break;
case VDISK_DEVICE_DETECTED:

224页
printf("Can't get A20 line Status; VDISK device detected/n");
break;
default:
printf("Can't get A20 line Status; unexpected result 0*%.4X/n", result);
break;
}
if(A20_StatuS == 0) /* Disabled */
{
result = alter_a20(GLOBAL_A20_LINE|ENABLE_A20_LINE);
switch(result)
{
case SUCCESS:
printf("A20 line globally enabled/n");
A20_Status=1;
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf( "Function not implemented; Couldn't enable A20 line/n" ) ;
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't enable A20 line/n");
break;
case A20_ERROR_OCCURRED:
printf("Error occurred while attempting to enable A20 line/n");
break;
Case ILLEGAL_PARAMETER:
printf("Illegal angument to alter_a20()/n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20( )/n", result);
break;
}
}
if(A20_statuS == 1) /* Engbled */
{
int k;
int m;
memset(test_buffer, 0, KILOBYTE);
hma_move (KILOBYTE, COPY_FROM_HMA, 0, test_buffer);
for(k=0; k<(KILOBYTE/16); k++)
{
printf("HMA:%.3X0", k);
for(m=0; m<16; m++)
printf("%.2X", test_buffer[(k<<4) + m]);
for(m=0; m<16; m++)
{
if(isprint(test_buffer[(k<<4) + m]))
 printf("%c", test_buffer[(k<<4) + m]);
else
printf(".");
}
printf("/n");
}
if(original_A20_Status==0)
{
result = alter_a20 (GLOBAL_A20_LINE | DISABLE_A20_LINE);
switch(result)
{
case SUCCESS:
printf("A20 line globally disabled/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented; disable A20 line/n");

225页
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; disable A20 line/n");
break;
case A20_ERROR_OCCURRED:
printf("Error while attempting to disable A20 line/n");
break;
case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()/n");
break;
case A20_LINE_STILL_ENABLED:
printf("A20 line still enabled/n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()/n",
result);
break;
}
}
}
}
}
void test_xms(void)
{
struct XMS_memory_status status;
struct bstat block_status;
struct XMS_move_data move_block;
unsigned short handle;
unsigned short result;
unsigned long address;
int move_Ok;
xms_avail(&status);
printf("Largest block of XMS memory available = %.4X Kbytes/n",
status.largest_block);
printf("Total free memory = %.4X Kbytes/n",
status.total_free_memory);
printf("Error status = %.4X/n", status.error_status);
if(status.error_status)
return;
result = xms_alloc(status.largest_block, &handle);
switch(result)
{
case SUCCESS:
printf("Memory allocated; handle = %.4X/n", handle);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_alloc/n");
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc/n");
return;
case ALL_EXTENDED_MEMORY_ALLOCATED:
printf("All memory allocated: xms_alloc/n");
return;
case ALL_EXTENDED_MEMORY_HANDLES_USED:
printf("All memory handles in use: xms_alloc/n");
return;
default:
printf("Unexpected result from xms_alloc: %.4X/n", result);
return;
}

226页
result = xms_realloc(handle, status.largest_block/2);
switch(result)
{
case SUCCESS:
printf("Block successfully reallocated in half/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_realloc not implemented/n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_realloc/n");
 break;
Case ALL_EXTENDED_MEMORY_ALLOCATED:
printf("xms_realloc failed: all extended memory allocated/n");
break;
case ALL_EXTENDED_MEMORY_HANDLES_USED:
printf("xms_realloc failed: all extended memory handles in use/n");
break;
Case INVALID_HANDLE:
printf("xms_realloc failed: invalid handle/n");
break;
case HANDLE_IS_LOCKED:
printf("xms_realloc failed: handle is locked/n");
break;
default:
printf("UnexpeCted result from xms_realloc: 0*%.4X/n", result);
break;
}
memset(test_buffer, 0*55, KILOBYTE);
move_block.transfer_size = KILOBYTE;
move_block.source_handle = 0;
move_block.source_offset.c_a.conventional_offset = FP_OFF((void far*)test_buffer);
move_block.source_offset.c_a.conventional_segment=FP_SEG((void far*)test_buffer);
move_block.destination_handle = handle;
move_block.destination_offset.long_Offset = 0L;
result = xms_move(&move_block);
move_ok = 0;
switch(result)
{
case SUCCESS:
printf("Data successfully written to XMS/n");
move_ok=1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_move not implemented/n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc/n");
break;
Case INVALID_source_HANDLE:
printf("Invalid source handle/n");
break;
case INVALID_SOURCE_OFFSET:
printf("Invalid source Offset/n");
break;
case INVALID_DESTINATION_HANDLE:
printf("Invalid destination handle/n");
break;
Case INVALID_DESTINATION_OFFSET:
printf("Invalid destination offset/n");
break;
Case INVALID_LENGTH:
printf("Invalid Offset/n");
break;

227页
case INVALID_OVERLAP:
printf("Invalid overlap/n");
break;
case PARITY_ERROR:
printf("Parity Error/n");
break;
default:
printf("Unexpected result from xms_move(): 0*%.4X/n");
break;
}
if(move_ok)
{
memset(test_buffer, 0*AA, KILOBYTE);
move_block.transfer_size = KILOBYTE;
move_block.source_handle = handle;
 move_block.source_offset.long_offset=0L;
move_block.destination_handle = 0;
move_block.destination_offset.c_a.conventional_offset =
FP_OFF((void far*)test_buffer);
move_block.destination_offset.c_a.conventional_segment=
FP_SEG((void far*)test_buffer);
result = xms_move(&move_block);
move_ok=0;
switch(result)
{
case SUCCESS:
printf("Data successfully read from XMS/n");
move_Ok = 1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_move not implemented/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc/n");
break;
Case INVALID_SOURCE_HANDLE:
printf("Invalid source handle/n");
break;
Case INVALID_SOURCE_OFFSET:
printf("Invalid source offset/n");
break;
Case INVALID_DESTINATION_HANDLE:
printf("Invalid destination handle/n");
break;
case INVALID_DESTINATION_OFFSET:
printf("Invalid destination offset/n");
break;
case INVALID_LENGTH:
printf("Invalid offset/n");
break;
case INVALID_OVERLAP:
printf("Invalid overlap/n");
break;
case PARITY_ERROR:
printf("Parity Error/n");
break;
default:
printf("Unexpected result from xms_move():0*%.4X/n");
break;
}
if(move_Ok)
{
int k;
228页
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*55)
{
printf("Error: corrupted data at Offset 0*%.4X; data = %.2X/n",
k, test_buffer[k]);
move_ok=0;
}
}
if(move_ok)
printf("Data read from XMS verified OK/n");
}
}
result = xms_lock(handle, &address);
switch(result)
{
 case SUCCESS:
printf("Block locked; linear address = %.81X/n",  address);
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_lock/n");
break;
case VDISK_DEViCE_DETECTED:
printf("VDISK device detected: xms_lock/n");
break;
Case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
Case LOCK_COUNT_OVERFLOW:
printf("Lock count overflowed/n");
break;
case LOCK_FAILED:
printf("Lock failed/n");
break;
default:
printf("Unexpected result from xms_lock: %.4X/n",  result);
break;
}
result = xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_free/n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_free/n");
break;
Case INVALID_HANDLE:
printf("Handle iS invalid/n");
break;
Case HANDLE_IS_LOCKED:
printf("Handle iS locked/n");
break;
default:
printf("Unexpected result from xms_free: %.4X/n", result);
break;
}
result = xms_bstat(handle, &block_status);
switch(result)
{
case SUCCESS:

229页
printf("LOCK count = %d/n", block_status.lock_count);
printf("Free handle count = %d/n", block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
 printf("Function not implemented: xms_bstat/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_bstat/n");
break;
Case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
default:
 printf("Unexpected result from xms_bstat: %.4X/n", result);
break;
}
result = xms_lock(handle, &address);
switch(result)
{
case SUCCESS:
printf("Block locked; linear address = %.81X/n", address);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_lock/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:  xms_lock/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
case LOCK_COUNT_OVERFLOW:
printf("Lock count overflowed/n");
break;
case LOCK_FAILED:
printf("LOCK failed/n");
break;
default:
printf("Unexpected result from xms_lock: %.4X/n", result);
break;
}
result = xms_bstat(handle, &block_status);
switch(result)
{
case SUCCESS:
printf("LOCK count = %d/n",  block_status.lock_count);
printf("Free handle count = %d/n",  block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_bstat/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_bstat/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
default:
printf("Unexpected result from xms_bstat: %.4X/n", result);
break;
}
result = xms_unlock(handle);
switch(result)
{
case SUCCESS:

230页
printf("Block unlocked/n");
 break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_unlock/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_unlock/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
case BLOCK_NOT_LOCKED:
printf("Block was not locked/n");
break;
default:
printf("Unexpected result from xms_unlock:%.4X/n",result);
break;
}
result=xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_free/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device deteCted:xms_free/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
case HANDLE_IS_LOCKED:
printf("Handle is locked/n");
break;
default:
printf("Unexpected result from xms_free:%.4X/n",result);
break;
}
result=xms_bstat(handle,&block_status);
switch(result)
{
case SUCCESS:
printf("Lock count=%d/n",block_status.lock_count);
printf("Free handle count=%d/n",block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_bstat/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_bstat/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
default:
printf("Unexpected result frOm xms_bstat:%.4X/n",result);
break;
}
result=xms_unlock(handle);
switch(result)

231页
{
case SUCCESS:
printf("Block unlocked/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
 printf("Function not implemented:xms_unlock/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_unlock/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
case BLOCK_NOT_LOCKED:
 printf("Block was not locked/n");
break;
default:
printf("Unexpected result from xms_unlock:%.4X/n",result);
break;
}
result=xms_bstat(handle,&block_status);
switch(result)
{
case SUCCESS:
 printf("Lock count=%d/n",block_status.lock_count);
printf("Free handle Count=%d/n",block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
 printf("FunctiOn not implemented:xms_bstat/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_bstat/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
default:
printf("Unexpected result from xms_bstat:%.4X/n",result);
 break;
}
result=xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_free/n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_free/n");
break;
case INVALID_HANDLE:
printf("Handle is invalid/n");
break;
Case HANDLE_IS_LOCKED:
printf("Handle is locked/n");
break;
default:
printf("Unexpected result from xms_free:%.4X/n",result);
break;
}
}

232页
void test_umb(void)
{
unsigned short status;
unsigned short size;
struct umb_data umb;
status=umb_alloc(0xFFFF,&umb);
switch(status)
{
case SUCCESS:
printf("We somehow allocated 1Mb of UMBs!/n");
 return;
case FUNCTION_NOT_IMPLEMENTED:
printf("UMB alloc not implemented.../n");
return;
case SMALLER_UMB_AVAILABLE:
size=umb.segment_size;
printf("Smaller UMB available:0x%.4X paragraphs/n",size);
break;
case NO_UMBS_AVAILABLE:
printf("Sorry,no UMBS available/n");
return;
default:
printf("Unexpected result from umb_alloc():0x%.4X/n",status);
return;
}
status=umb_alloc(size, &umb);
switch(status)
{
case SUCCESS:
printf("0x%.4X paragraphs allocated at %.4X:0000/n",umb.Segment_size
,
umb.segment_number);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("UMB alloc not implemented.../n");
return;
case SMALLER_UMB_AVAILABLE:
size=umb.segment_size;
printf("Smaller UMB available:0x%.4X paragraphs/n",size);
return;
case NO_UMBS_AVAILABLE:
printf("Sorry,no UMBs available/n");
return;
default:
printf("Unexpected result from umb_alloc():0x%.4X/n",status);
return;
}
status=umb_free(umb.segment_number);
switch(status)
{
case SUCCESS:
printf("UMB successfully freed/n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("umb_free not implemented.../n");
break;
case INVALID_UMB_SEGMENT:
printf("Invalid Segment/n");
 break;
default:
printf("Unexpected result from umb_free:0x%.4X/n",status);
break;
}
}
233页
      int main()
            {
            unsigned short          result;
            struct XMS_status        status;
            i

【上篇】
【下篇】

抱歉!评论已关闭.