原创 DOS下DEBUG,XMS,EMS,DPMI,DOS4GW研究

2011-4-9 13:31 5991 7 7 分类: 工程师职场

DOS下DEBUG,XMS,EMS,DPMI,DOS4GW研究

wxleasyland

2011.2

 

本文引用了很多资料。

一、           概念

实模式和保护模式

在386以上档次的微机中,有两种存储器工作方式,一种称为实地址方式或实方式,另一种称为保护方式。

在实方式下,物理地址仍使用20位,所以最大寻址空间为1MB,以便与8086兼容。

保护方式采用32位物理地址,寻址范围可达4GB。

DOS系统在实方式下工作,它管理的内存空间仍为1MB,因此它不能直接使用扩展存储器。

 

EMS:扩充内存(Expanded Memory)。

1985年初,Lotus、Intel和Microsoft三家共同定义了LIM-EMS,即扩充内存规范,通常称EMS为扩充内存。80386DX已经是32位微处理器,实际寻址能力达4GB,反观旧的内存管理规范却处处碍手碍脚,于是扩充内存Expanded memory规范横空出世,取代了INT 15H,使程序能够访问32MB的内存空间。EMS通过一种页面映射分配技术的反复映射访问所有的EMS内存,这样的效率并不高,所以只有少数老游戏使用了EMS。EMS的原理和XMS不同,它采用了页帧方式。页帧是在1MB空间中指定一块64KB空间(通常在保留内存区内,但其物理存储器来自扩展存储器),分为4页,每页16KB。EMS存储器也按16KB分页,每次可交换4页内容,以此方式可访问全部EMS存储器。符合EMS的驱动程序很多,常用的有EMM386.EXE、QEMM、TurboEMS、386MAX等。DOS和Windows中都提供了EMM386.EXE。emm386.exe可以把扩展内存(XMS)仿真成扩充内存(EMS),以满足一些要求使用扩充内存的程序。

 

XMS:扩展内存Extended memory。

由于EMS并没有解决根本的问题,Microsoft又制定了扩展内存Extended memory管理规范XMS,通过在实模式与保护模式之间的快速切换,使程序在保护模式中能够直接使用系统所有内存,从而快速访问XMS。XMS简单而实际,被众多DOS游戏采用。很多游戏采用了一个signed integer来存储XMS的数量,导致32767以上的数值变成负数,这就要求我们降低内存数量到32MB了。Microsoft的HIMEM.SYS是一个符合XMS规范的管理扩展内存的驱动程序。但在这种方式下,扩展内存只能作为数据区使用,运行于实地址模式下的DOS及其应用程序代码不能存放其中。

 

注:物理内存都是一样的,EMS和XMS只是操作系统管理内存的方式不一样而已。EMS方式更早出现,XMS方式是更迟出现的!!但是XMS更简单好用,所以Microsoft就没有直接支持EMS了,而是直接支持XMS,DOS里加载HIMEM.SYS就是XMS了。如果要EMS,则要再加载EMM386.EXE,将XMS仿真成EMS!

 

保留内存:占据640KB~1024KB地址空间。

分配给显示缓冲存储器、各适配卡上的ROM和系统ROM BIOS,剩余空间可作上位内存UMB。UMB的物理存储器取自物理扩展存储器。此范围的物理RAM可作为Shadow RAM使用。

(我注:我个人觉得是这样:

在640KB以下时,地址是分给了内存,CPU地址线在这范围内时,就是选通到内存去了。

640KB~1024KB地址是分给了ROM等,而不是给内存,所以CPU地址线在这范围内时,硬件自动选通的是去ROM、显卡内存等地方,而不是去内存。这样,由于硬件设计的原因,这部分300多KB的物理内存容量就相当于废掉了!

保护模式下,CPU地址线在1MB以上时,就又选通到去内存了。比如XMS或EMS管理时。

也就是说:

如果只装了1MB的物理内存条,只用到了640KB,其它300多KB的物理容量都废掉了,这300多KB也不能用作XMS或EMS!

如果只装了2MB的物理内存条,XMS或EMS也只能用到1MB物理容量,即1MB-2MB之间的物理内存空间,而640KB-1024KB之间的物理空间也是用不到的!

EMS有映射软件地址的功能,即可以将地址在640KB-1024KB之间的没用到的空间映射到EMS管理的1MB以上的物理空间去!即EMS虚拟出了UMB。

比如,软件访问640KB-1024KB之间的地址,实际上被EMS给转到1MB以上的物理空间去了。所以这没有涉及到硬件上,是从软件上实现的。

也就是说,地址在1MB以上物理空间中需要再挪用300多KB空间映射给UMB使用。而原来地址在640-1024KB之间的物理内存容量仍是不能用的。

这也可以用MEM命令看出来。2MB的物理内存条,MEM一看,总内存只有1.6MB多,就是这个原理。那300多KB是在硬件上就被废掉了!

在未映射前,DEBUG对这300多KB空间进行写入,也是写不了的,写了无效,相当于只读。

 

SHADOW(影子)内存

可能还会发现一个问题:即是对于装有1MB或1MB以上物理内存的机器,其640KB~1024KB这部分物理内存如何使用的问题。由于这部分地址空间已分配为系统使用,所以不能再重复使用。为了利用这部分物理内存,在某些386系统中,提供了一个重定位功能,即把这部分物理内存的地址重定位为1024KB~1408KB。这样,这部分物理内存就变成了扩展内存,当然可以使用了。但这种重定位功能在当今高档机器中不再使用,而把这部分物理内存作为Shadow存储器。Shadow存储器可以占据的地址空间与对应的ROM是相同的。Shadow由物理内存条的RAM组成,其速度大大高于ROM。即把ROM中的内容(如各种BIOS程序等)装入相同地址的Shadow RAM中,就可以从RAM中访问BIOS,而不必再访问ROM。这样将大大提高系统性能。因此在设置CMOS参数时,应将相应的Shadow区设为允许使用(Enabled)。

(我注:SHADOW后,即使空间上没有对应的ROM,SHADOW的这部分RAM空间DOS也用不了,因为DOS不知道这个是SHADOW RAM,仍当它是不可以存取的空间,不会把程序LH到这里。如果用EMS将空间变成UMB后,DOS就知道UMB可以用,可以把程序LH到这里了)

 

HMA:“高内存区”(High Memory Area—缩写HMA)。

扩展内存的第一段,即1MB到1MB+64KB的区域称为HMA。用户可以要求把常规内存中的DOS大部分常驻程序移到高内存区,以腾出更多的空间让用户的应用程序使用。在实方式下,内存单元的地址可记为:段地址:段内偏移。通常用十六进制写为XXXX:XXXX。实际的物理地址由段地址左移4位再和段内偏移相加而成。若地址各位均为1时,即为FFFF:FFFF。其实际物理地址为:FFF0+FFFF=10FFEF,约为1088KB(少16字节),这已超过1MB范围进入扩展内存了。这个进入扩展内存的区域约为64KB,是1MB以上空间的第一个64KB。我们把它称为高端内存区HMA(High Memory Area)。HMA的物理存储器是由扩展存储器取得的。因此要使用HMA,必须要有物理的扩展存储器存在。此外HMA的建立和使用还需要XMS驱动程序HIMEM.SYS的支持,因此只有装入了HIMEM.SYS之后才能使用HMA。

(我注:即CPU在实模式下,按地址总线访问,访问不到这64KB内容!但程序的内存单元地址写法可以写,所以程序可以放在这个地址内。当存取这64KB时,实际是经HIMEM切换到保护模式去访问这64KB的数据内容的)

 

UMB(Upper Memory Blocks)称为上位内存或上位内存块。

它是由挤占保留内存中剩余未用的空间而产生的,它的物理存储器仍然取自物理的扩展存储器,它的管理驱动程序是EMS驱动程序。为了解释上位内存的概念,我们还得回过头看看保留内存区。保留内存区是指640KB~1024KB(共384KB)区域。这部分区域在PC诞生之初就明确是保留给系统使用的,用户程序无法插足。但这部分空间并没有充分使用,因此大家都想对剩余的部分打主意,分一块地址空间(注意:是地址空间,而不是物理存储器)来使用。于是就得到了又一块内存区域UMB。

(我注:EMS的功能就是映射页面,所以可以将这部分未用的地址空间映射到EMS内存中去,就变成可以访问这部分的空间的内容了)

 

DPMI、DOS扩展程序和DOS4GW:

DPMI是DOS保护模式接口(DOS Protected Mode Interface)的英文缩写,是在DOS平台下实现大内存编程的重要手段。DPMI它提出了Real Mode(实模式)和Protected Mode(保护模式)的概念。所谓Real Mode,就是DOS默认的工作模式,在这种模式下DOS最多只能管理640KB内存。而Protected Mode是由DPMI平台提供的一种新模式,在这种模式下DOS可以管理16MB甚至更多的内存空间,这对于当时的计算机应用已经绰绰有余(事实上,超过16MB内存的计算机系统一直到1997年才得以普及)。Protected Mode的实现方法,首先需要指出,Protected Mode只是一个抽象概念,其具体实现依赖于DOS Extender(关于DOS Extender,还没有统一的中文翻译)。DOS Extender是DPMI的具体应用,可以将它理解为一个程序,和Real Mode下的其它应用程序一样,它占用640KB中的一部分,但是它可以让经过特殊设计的应用程序以它为平台运行。这样,这些应用程序就可以通过DOS Extender访问高于640KB的内存空间。常见的DOS Extender包括DOS/16M、DOS/4G、DOS/4GW、DOS32A、Causeway等,它们都通过DPMI实现对大内存的管理。Watcom C/C++是80386时代风靡全球的C语言开发工具,DOS/4GW是DOS/4G为当时如日中天的Watcom C/C++制作的特别版,售价比较低,代价是最多只能管理16MB内存(不过在当时已经足够了)。在众多实现DOS大内存程序设计的方法中,Watcom C/C++与DOS/4GW的搭配是最受欢迎的。Watcom C/C++拥有无与伦比的程序执行效率;而DOS/4GW不仅稳定,功能也十分强大(由于运行在保护模式下的程序往往在访问硬件时遇到困难,DOS/4GW比较好地解决了这个问题)。

对于一些大型应用程序,如AutoCAD 11 0 12.0、3DS 3.04.0、Lotus 1-2-3以及一些大型游戏程序来说,XMS和EMS的方式都不太适合。于是,它们采用了第三种方式, 即依靠称为DOS扩展程序的软件工具来使用扩展内存。DO S扩展程序通过开辟保护模式运行方式使用户程序可以在扩展内存中运行和存取数据。比较著名的DOS扩展程序有PharLap Software's386 DOS-Extender、Rational System's Dos/16M、DOS4GW 等,Lotus 1-2-3 3.0以上版本和AutoCAD11.0 12.0就是前两个扩展程序的很流行的应用程序。DOS4GW 常被用于游戏程序作为其DOS扩展程序。DOS4GW实际上是一个DOS扩展程序,它通过开辟保护模式运行方式,使用户程序得以在扩展内存中运行和存取数据。

人们终于找到了理想的突破640K结界的方法:使用DOS保护模式(DOS Protected Mode)。80386及以后的CPU在电脑启动后都会进入实模式(Real Mode),以兼容早期的8088。当运行在保护模式中时,CPU的直接寻址能力高达4GB,并且提供了很多高级功能,使程序可以直接使用系统所有的内存资源,常规内存、扩充内存和扩展内存统统失去了意义;并且,保护模式能够充分发挥32位CPU的威力,极大的提高了效率。常玩游戏的各位一定对DOS4GW非常熟悉,它是Watcom C/C++专用的内存管理工具,可以使程序运行在32位保护模式下。著名的《金庸群侠传》就是用了这个DOS扩展器。运行在32位保护模式下的游戏很少遇到内存问题,只可惜这样的游戏真是不多啊。不管扩充内存或扩展内存有多大,DOS的应用程序只能在常规内存下运行。有的程序可以通过DOS扩展器(比如DOS4GW.exe等程序)使CPU进入保护模式,从而直接访问扩展内存;但是要注意,进入保护模式以后,计算机就脱离了DOS状态。

DOS4GW是远古时代的一个支持保护模式编程的平台,它可以使程序突破DOS的640K内存限制,达到4GB(也就是跟WINDOWS一样啦),因此它很适用于开发游戏程序。用它来开发的语言一般用Watcom C++。以前的DOS图形程序都要用的这个东东,可以理解为DOS下的DirectX,比如一些游戏,还有像SEA这样的看图软件都用它,它其实是DPMI(支持保护模式的接口)的一种,在DOS下,为了突破640K内存的限制,许多公司(组织)定义了这个协议,现在已经至少在4.0版本以上了。它其实也是中断,你只要直接调用它就可以了。使用它的最大好处就是不用担心内存不足。现在用的开发平台Watcom C++,它直接使用DPMI,所以可以直接使用calloc函数,而由编译器去链接。不过Dos4gW是单独使用,编译后必须要有它的支持,所以可以使用pmodew,它有Dos4gw的大部分功能,但是编译后它是与执行程序捆绑在一起的,不用pmodew的支持。当然你编译的程序是32位程序了,所以当要调用实模式的中断时就必须通过一种方式去调用。

DJGPP是一种 DOS下的 32位 C/ C+ + 保护模式软件的开发环境,它通过 DPMI ( DOS保护模式接口 )实现DOS下 32位保护模式软件的开发。

 

我注:即DOS扩展程序实现了DPMI协议。

DOS用户软件->DOS扩展程序(实现了DPMI保护模式)->高于1MB的物理内存

DPMI是很难的,一般不需要去具体了解它,只要知道DOS扩展程序怎么用就可以。

见后面编写DOS4GW程序。

 

总结:

即:DOS都是在实模式下常规内存中运行!也就是说程序运行都是在640K空间里,数据什么的可以放在1MB以上的空间里。

DOS(实模式)常规内存->XMS接口->HIMEM.SYS(切换成保护模式)->高于1MB的物理内存

DOS(实模式)常规内存->EMS接口->EMM386->HIMEM.SYS(切换成保护模式)->高于1MB的物理内存

 

 

二、           debug中内存管理EMS

命令有XA,XD,XM,XS。需要有EMS。不然会显示ems not installed。

页映射:EMS是用页映射的方式工作的,一页16KB!即将地址在1MB以上的一页容量映射到常规内存或保留内存的相应页中。映射完后,对这16KB的访问,相当于是对地址1MB以上的那一页容量的访问。

逻辑页logical page:指EMS中的1MB以上的页!

物理页physical page:指常规内存或保留内存的中的页!

句柄handle:有点难理解,其实很简单,就像打开一个文件得到一个句柄一样。

 

XS 显示状态:

Handle 0000 has 0000 pages allocated

Physical page 04 = Frame segment 4000

Physical page 05 = Frame segment 4400

。。。共有32个物理页,512KB

可见:4000:0000是页号04的物理页,4400:0000是页号05的物理页(十六进制)

 

XA 分配EMS页面:

这时打 XA 1F,表示分配1F个EMS逻辑页面!注意:是十六进制,另外,是一次性分配1F个逻辑页面。这时,显示:

Handle created = 0001

这样就创建了一个句柄,句柄号0001。即这个0001号句柄带了1F个页面!所有的逻辑页面本来就是存在的,只是分配了其中的1F个页面给这个0001号句柄使用!

这1F个逻辑页面的逻辑页号是00――1E!即logical page 00 到 logical page 1E。

 

XM 映射页面:

这时打XM 1E 4 1,显示:

Logical page 1E mapped to physical page 04

即1号句柄中的“1E号逻辑页”映射到“04号物理页”(4000:0000)中了!

这时,对4000:0000访问,就是访问1号句柄的1E号EMS逻辑页。

注意:映射过来后,原4000:0000的内容就被覆盖了!

       一个逻辑页可以映射到多个物理页中去,一个物理页中改了,其它物理页也改了

       一个物理页中只能映射一个逻辑页,如果映射一个新逻辑页,则老的逻辑页就自动不映射了(老的逻辑页的内容还在)。

 

XD 删除句柄:

XD 1 就删除了1号句柄,释放了EMS内存页。

 

 

三、           使用XMS

可参考《使用XMS扩展内存编程实例》(http://blog.csdn.net/TFTJT/archive/2010/11/04/5987123.aspx),只要HIMEM.SYS就可以了。HIMEM.SYS是XMS的驱动程序。

用法其实简单:

1.        HIMEM.SYS在INT 2FH中添加了服务程序,先用INT2FAX4300检测是否存在XMS:

----------2F4300-----------------------------

INT 2F - EXTENDED MEMORY SPECIFICATION (XMS) - INSTALLATION CHECK

       AX = 4300h

Return: AL = 80h XMS driver installed

       AL <> 80h no driver

Notes:    XMS gives access to extended memory and noncontiguous/nonEMS memory

         above 640K

       this installation check DOES NOT follow the format used by other software

 

2.        再获得驱动程序的远程调用地址,以后远程调用这个地址,就可以使用XMS功能了。

----------2F4310-----------------------------

INT 2F - EXTENDED MEMORY SPECIFICATION (XMS) - GET DRIVER ADDRESS

       AX = 4310h

Return: ES:BX -> driver entry point (这个地址就是函数入口了)

 

3.        远程调用时,AH代表了不同的功能,常用08H(询问),09H(分配),0BH(移动),0AH(释放):

Perform a FAR call to the driver entry point with AH set to the function code

     AH   function

     00h  Get XMS version number

          Return: AX = XMS version (in BCD, AH=major, AL=minor)

                   BX = internal revision number

                   DX = 0001h if HMA (1M to 1M + 64K) exists

                          0000h if HMA does not exist

     01h  Request High Memory Area (1M to 1M + 64K)

          DX = memory in bytes (for TSR or device drivers)

                FFFFh if application program

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,90h,91h,92h) (see below)

     02h  Release High Memory Area

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,90h,93h) (see below)

     03h  Global enable A20, for using the HMA

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,82h) (see below)

     04h  Global disable A20

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,82h,94h) (see below)

     05h  Local enable A20, for direct access to extended memory

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,82h) (see below)

     06h  Local disable A20

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,82h,94h) (see below)

     07h  Query A20 state

          Return: AX = 0001h enabled

                        = 0000h disabled

                   BL = error code (00h,80h,81h) (see below)

     08h  Query free extended memory, not counting HMA (得到空闲的XMS数量)

          BL = 00h (some implementations leave BL unchanged on success)

          Return: AX = size of largest extended memory block in K (XMS大小,以KB计,AX值是无符号的,如AX=B380H,表示45952KB,超过了64MB就是AX=FFFFH。如果C程序中用有符号变量,则会产生负数)

                   DX = total extended memory in K

                   BL = error code (00h,80h,81h,A0h) (see below)

     09h  Allocate extended memory block (分配XMS内存)

          DX = Kbytes needed

          Return: AX = 0001h success

                           DX = handle for memory block

                        = 0000h failure

                           BL = error code (80h,81h,A0h) (see below)

     0Ah  Free extended memory block  (释放XMS内存)

          DX = handle of block to free

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,A2h,ABh) (see below)

     0Bh  Move extended memory block  (移动内存块数据,数据长度必须是偶数!)

          DS:SI -> EMM structure (see below)

          Note: if either handle is 0000h, the corresponding offset is

                 considered to be an absolute segment:offset address in

                 directly addressable memory见后述

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h-82h,A3h-A9h) (see below)

     0Ch  Lock extended memory block

          DX = handle of block to lock

          Return: AX = 0001h success

                           DX:BX = 32-bit linear address of locked block

                        = 0000h failure

                           BL = error code (80h,81h,A2h,ACh,ADh) (see below)

     0Dh  Unlock extended memory block

          DX = handle of block to unlock

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,A2h,AAh) (see below)

     0Eh  Get handle information

          DX = handle for which to get info

          Return: AX = 0001h success

                           BH = block's lock count

                           BL = number of free handles left

                           DX = block size in K

                        = 0000h failure

                           BL = error code (80h,81h,A2h) (see below)

     0Fh  Reallocate extended memory block

          DX = handle of block

          BX = new size of block in K

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,81h,A0h-A2h,ABh) (see below)

     10h  Request upper memory block (nonEMS memory above 640K)

          DX = size of block in paragraphs

          Return: AX = 0001h success

                           BX = segment address of UMB

                           DX = actual size of block

                        = 0000h failure

                           BL = error code (80h,B0h,B1h) (see below)

                           DX = largest available block

     11h  Release upper memory block

          DX = segment address of UMB to release

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,B2h) (see below)

     12h  (XMS v3.0) Reallocate upper memory block

          DX = segment address of UMB to resize

          BX = new size of block in paragraphs

          Return: AX = 0001h success

                        = 0000h failure

                           BL = error code (80h,B0h,B2h) (see below)

     34h  (QEMM 5.11 only, undocumented) ???

     44h  (QEMM 5.11 only, undocumented) ???

     88h  (XMS v3.0) Query free extended memory

          Return: EAX = largest block of extended memory, in K

                   BL = status

                         00h success

                         80h not implemented (i.e. on a 286 system)

                         81h VDISK detected

                         A0h all extended memory allocated

                        ECX = physical address of highest byte of memory

                                     (valid even on error codes 81h and A0h)

                   EDX = total Kbytes of extended memory (0 if status A0h)

     89h  (XMS v3.0) Allocate any extended memory

          EDX = Kbytes needed

          Return: AX = 0001h success

                                DX = handle for allocated block (free with AH=0Ah)

                        = 0000h failure

                            BL = status (80h,81h,A0h,A1h,A2h) (see below)

     8Eh  (XMS v3.0) Get extended EMB handle information

          DX = handle

          Return: AX = 0001h success

                                BH = block's lock count

                            CX = number of free handles left

                            EDX = block size in K

                            = 0000h failure

                                BL = status (80h,81h,A2h) (see below)

     8Fh  (XMS v3.0) Reallocate any extended memory block

          DX = unlocked handle

          EBX = new size in K

          Return: AX = 0001h success

                            = 0000h failure

                            BL = status (80h,81h,A0h-A2h,ABh) (see below)

Notes:   HIMEM.SYS requires at least 256 bytes free stack space

     the XMS driver need not implement functions 10h through 12h to be

       considered compliant with the standard

 

Format of EMM structure:

Offset   Size  Description

 00h      DWORD   number of bytes to move (must be even) (要移动的数据长度,必须是偶数!

 04h      WORD      source handle                               源句柄

 06h      DWORD   offset into source block              偏移地址

 0Ah     WORD      destination handle                       目的句柄

 0Ch     DWORD   offset into destination block      偏移地址

Notes:   if source and destination overlap, only forward moves (source base

       less than destination base) are guaranteed to work properly

     if either handle is zero, the corresponding offset is interpreted

       as a real-mode address referring to memory directly addressable

       by the processor

(句柄号如果是0,则表明是常规内存,偏移地址的形式为“段地址:偏移地址”)

(如果句柄号非0,则是XMS的1MB以上的空间,这时偏移地址就是原样地址,从00000000H-FFFFFFFFH,没有段地址和偏移地址的概念!!例如,申请了1MB的XMS空间,其偏移地址范围就是00000000H-000FFFFFH)

 

Error codes returned in BL:

     00h successful

     80h function not implemented

     81h Vdisk was detected

     82h an A20 error occurred

     8Eh a general driver error

     8Fh unrecoverable driver error

     90h HMA does not exist

     91h HMA is already in use

     92h DX is less than the /HMAMIN= parameter

     93h HMA is not allocated

     94h A20 line still enabled

     A0h all extended memory is allocated

     A1h all available extended memory handles are allocated

     A2h invalid handle

     A3h source handle is invalid

     A4h source offset is invalid

     A5h destination handle is invalid

     A6h destination offset is invalid

     A7h length is invalid

     A8h move has an invalid overlap

     A9h parity error occurred

     AAh block is not locked

     ABh block is locked

     ACh block lock count overflowed

     ADh lock failed

     B0h only a smaller UMB is available

     B1h no UMB's are available

     B2h UMB segment number is invalid

 

 

四、           使用EMS

可参考《EMS在C里面地运用一例》(http://www.mcxb.com/sysProgram/Clanguage/CSL/53902_3.html),需要HIMEM.SYS、EMM386.EXE。EMM386.EXE是EMS的驱动程序。

原理是EMS驱动程序在DOS中安装了INT 67H服务程序,我们调用INT 67H就好了。

但是,如果没有EMS,直接用INT 67H来检测EMS是否存在是不行的,会死机。要用别的方法来检测。

1.        检测是否存在EMS:

EMS驱动程序安装后,会在DOS中加入一个名称为"EMMXXXX0"的设备,只要检测这个设备是否存在就可以了。

方法一:可以用INT21H AX=3D00H,打开文件的方式,打开设备"EMMXXXX0",如果可以打开,说明"EMMXXXX0"存在,即EMS存在,再INT21H AH=3EH关掉这个句柄就好了。

经试验,成功。如果有NOEMS参数,则打开设备失败。这个方法最保险。

方法二:可以用DOS未公开的中断INT21H AH=52H,得到 "INVARS",在"INVARS"中得到,DEVICE HEADER 链表,查询这个链表,看是否有"EMMXXXX0"存在。比较麻烦。

这个方法可以找到所有的DOS设备。

方法三:用中断INT 67H AX= FFA5H(来自《中断大全》)

----------67FFA5-----------------------------(经试验成功)

INT 67 - Microsoft EMM386.EXE v4.20+ - INSTALLATION CHECK

   AX = FFA5h

Return: AX = 845Ah if loaded(试验返回这个值)

       BX:CX -> API entry point

Notes: this call is available even if EMM386 is not providing EMS

   if no other program has hooked INT 67, an alternate installation

     check is to search for the string

     "MICROSOFT EXPANDED MEMORY MANAGER 386" early in the INT 67

     handler's segment, usually at offset 14h

SeeAlso: AH=3Fh

 

Call API entry point with:

   AH = 00h get memory manager's status

       Return: AH = status (即00h ON, 01h OFF, 02h AUTO)(试验,返回值是00)

                     bit 0: not active (OFF)

                        bit 1: in "Auto" mode

   AH = 01h set memory manager's state

       AL = new state (00h ON, 01h OFF, 02h AUTO) (试验,可以设成OFF,这样就不能用EMS了)

   AH = 02h Weitek coprocessor support

       AL = subfunction

          00h get Weitek support state

              Return: AL = status

                            bit 0: Weitek coprocessor is present

                        bit 1: Weitek support is enabled

          01h turn on Weitek support

          02h turn off Weitek support

     --- v4.20-4.41 only ---

   AH = 03h Windows support???

       AL = subfunction (00h, 01h)

   AH = 04h print copyright notice to standard output

           (using INT 21/AH=09h)

   AH = 05h print available report

           (the one shown when running EMM386 from the DOS prompt)

另外,经试验,如果有NOEMS参数,则也是返回845AH,而且状态也是AH=00H。但EMS实际是不能用的。这个方法不保险。

 

2.        调用INT 67H AH=40H看EMM状态是否正常,正常才行

----------6740-------------------------------(经试验成功)

INT 67 - LIM EMS - GET MANAGER STATUS

   AH = 40h

Return: AH = status

       00h successful

       80h internal error

       81h hardware malfunction

       84h undefined function requested by application

Note:  this call can be used only after establishing that the EMS driver is in

     fact present

 

3.        得到物理页的段地址

----------6741-------------------------------(经试验成功)

INT 67 - LIM EMS - GET PAGE FRAME SEGMENT

       AH = 41h

Return: AH = 00h function successful

           BX = segment of page frame  (返回 BX=D000H)

       AH = error code (see AH=40h)

 

4.        得到EMS内存页的数量

----------6742-------------------------------(经试验成功)

INT 67 - LIM EMS - GET NUMBER OF PAGES

       AH = 42h

Return: AH = 00h function successful

           BX = number of unallocated pages(返回 BX=0800H)

           DX = total number of pages(返回 DX=0818H)

       AH = error code (see AH=40h)

 

5.        分配EMS内存页,得到句柄

----------6743-------------------------------(经试验成功)

INT 67 - LIM EMS - GET HANDLE AND ALLOCATE MEMORY

       AH = 43h

       BX = number of logical pages to allocate

Return: AH = status

           00h function successful

                     这时DX = handle

           80h internal error

           81h hardware malfunction

           84h undefined function requested

           85h no more handles available

           87h more pages requested than physically exist

           88h more pages requested than currently available

           89h zero pages requested

 

6.        映射逻辑页到物理页

----------6744-------------------------------(经试验成功)

INT 67 - LIM EMS - MAP MEMORY

       AH = 44h

       AL = physical page number (0-3) (试验,页号4等也可以)

       BX = logical page number

       DX = handle

Return: AH = status

           00h function successful

           80h internal error

           81h hardware malfunction

           83h invalid handle

           84h undefined function requested

           8Ah invalid logical page number

           8Bh illegal physical-page number

物理页号有0-3,每页16KB,即上面得到的页段地址是D000H,则物理页0的段地址是D000H,物理页1的段地址是D400H,依此类推。另外,没映射前,D000段处的内容是写了也无效的,映射进逻辑页后,就可以写进去了。

另外,物理页号可以不一定是只有0-3,还有4,5,6...等其它页号,就如同在DEBUG中用的那样!如何得到物理页列表,见下述。

 

7.        释放EMS内存页

----------6745-------------------------------

INT 67 - LIM EMS - RELEASE HANDLE AND MEMORY

       AH = 45h

       DX = EMM handle

Return: AH = status

           00h successful

           80h internal error

           81h hardware malfunction

           83h invalid handle

           84h undefined function requested

           86h error in save or restore of mapping context

 

8.        另外,要得到物理页列表,如同DEBUG命令XS显示的一样

----------6758-------------------------------(经试验成功)

INT 67 - LIM EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY

       AH = 58h

       AL = subfunction

           00h get mappable physical address array(用这个数值)

              ES:DI -> buffer to be filled with array

           01h get number of entries in m.p.a. array

Return: CX = number of entries in array

       AH = status

           00h successful

           80h internal error

           81h hardware failure

           84h undefined function requested

           8Fh undefined subfunction

Note:      the returned array for subfunction 00h is filled in physical segment

         address order

Format of mappable physical address entry:这个列表的格式如下:

Offset    Size Description

 00h       WORD   physical page segment  物理页段地址

 02h       WORD   physical page number 物理页编号

得到的列表的结果如下:

1937:0200  00 40  04 00  00 44   05 00-  00 48  06 00  00 4C  07 00

          段地址 页号   段地址 页号  段地址 页号  段地址 页号 

 

 

----------6760-------------------------------(这个功能号经试验失败)

INT 67 - EEMS - GET PHYSICAL WINDOW ARRAY

       AH = 60h

       ES:DI -> buffer

Return: AH = status    (返回84H,表示未定义该功能)

       AL = number of entries

       buffer at ES:DI filled

 

 

 

五、           编写DOS下的DOS4GW程序

OpenWatcom C/C++ 是Watcom C/C++开放后的版本,它是免费、而且开放源码的。

去官网直接下OpenWatcom C/C++ 的WIN32版,最新是1.9版。安装后,WIN XP中就能用,图形界面,很方便,可以编制DOS下DOS4GW的程序。

使用方法可以见http://cpp.comsci.us/process/watcom.html

正常DOS下的程序是16BIT的,而DOS下的DOS4GW的程序,是32BIT的,一定要有DOS4GW.exe配合才能运行,不然运行不了。  OpenWatcom已经自带了DOS4GW.exe这个文件,而且有说明文档。

在OpenWatcom的IDE界面里,建新项目,选DOS-32BIT,就可以选DOS4GW EXECUTABLE了,这样就能编制DOS4GW的程序了。

经试验,OK!

也试了这个《VESA虚拟屏幕编程》:

http://www.linuxforum.net/forum/printthread.php?Cat=&Board=linuxK&main=401092&type=post

 

由于Watcom C/C++编出的程序是32位的,所以在编程时同Borland C/C++有所不同。要注意以下几点:

一、数据长度不同。在Borland C/C++3.1中,int数据是16位的。而在Watcom C/C++中,int数据则是32位的。

二,内存地址不同。32位保护模式下的内存(包括显存在内)都是经过重新影射过的。同实模式下的内存地址不同。如果使用实模式的显存地址进行读写,那后果则是无法预料的。所以在进行显存读写操作时,必须对显示地址进行转换之后才能使用。

三、中断调用方式不同。在32位保护模式下,不能使用int86、int86x进行中断调用。要使用int386、int386x进行中断调用。

 

另外我试了,好像char数组超过64KB就不行,而改用malloc()就可以分配出1MB以上的内存空间,再用指针比如char *访问就可以。终于可以使用大内存了。

 

DOS4GW的游戏就是这样写成的!!!

深入的就不会了,知道原理就可以了。

 

 

PARTNER CONTENT

文章评论0条评论)

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