保护模式指南v0.02author:TillGerken译者:ZivWangNovember21,2004目录零章译者前言和修订记录1译者前言2修订记录第一章简介第二章开始第三章表格、描述符、选择子和诸多令人困惑的表格第四章有趣的资料第五章例程缺少的部分第六章保护模式下的一点编程技巧第七章错误,陷阱和异常第八章有用的中断服务第九章虚拟DMA规范第十章虚拟控制程序接口(VCPI)空[翻译很糟糕,因为我对其一无所知]第十一章DOS保护模式接口(DPMI)第十二章另外的资料第十三章鸣谢和想法第十四章译者昀后的话零章译者前言和修订记录1译者前言只有在保护模式下,现在的计算机才能发挥出全部能力。但是关于保护模式中文资料屈指可数。一本是清华大学出版社出版的周明德主编的《保护方式下的80386及其编程》,我没有读过;另一本也是清华大学出版社出版的,扬季文主编的《80X86汇编语言程序设计教程》,也就是常提起的“黑皮书”,这本书我看过。保护模式讲的很多,但是上面的例子有错误,费很大力气才调试通过3个程序。此外,还有李彦昌的电子书《80x86保护模式系列教程》。这份电子文档上面说是参考了上面提到的两本书,尤其是和《8》这本书,内容非常非常相似,例子上的错误好像也是参考的结果。我想以后应当写一份关于这个电子文档的考证。在这个想法之后,看到了TillGerken撰写的“ProtectedModeTutorialv0.02”,遂打算先翻译这篇文章先。文章不算长,翻译大约用了2周时间。阅读中如果你有任何问题,请在的论坛上留言提问。文章中“[]”中的内容是我加上的,并不在原文中。文章中的程序经过我的修改,保证都能正常编译运行。编译环境是MASM6.11,在VMWARE4.5.2B8848FREEDOSBETA9中运行结果正确。2修订记录版本修订原因修订内容0.02从原文翻译暂时空白页第一章简介这是一篇的为保护模式初学者编写的教程,我尽量使其简短并尽可能做到循序渐进。本文介绍了编写保护模式程序所需的一切,诸如环境的要求,这也是为没有任何经验的人准备的。你所要做的只是用你头脑(我想你一定有,是吧?)理解,同时还需要你有一些汇编语言知识。你所需要的全部信息都在这份文档中,但是我不会对任何由于使用这份文档提供的资料而导致的任何直接的或者间接的损害负责。如果你将本文所示用于商业用途,请从所在地给我邮寄一张明信片(当然,如果你只是阅读本文也可以这样,但请不要用E-MAIL来代替!)我的地址是:TillGerkenWiefelstederStr.2a26127OldenburgGermany电子信箱:Till.Gerken@ngosub0.ngo.ol.ni.schule.deFidoBBS标号:[注1]2:2426/2190.16同样,如果你有任何的意见或者建议以及批评都可以通过上面的任何一种方式联系我。本文将解释80386保护模式基本原理,并提供一个完整的模式切换的源程序。这个小程序会向你展示保护模式的基本规则。为了便于理解和阅读,它并没有经过优化。如果你有扩展的问题或者建议,请给我发邮件。下面的代码使用汇编语言写成,遵循TASM2.01语法。对于TASM新版本来说,下面代码同样可以工作,也许只需要增加一个宏,将dwords转化为words以便编译。[译者:我已经以MASM6.11为准修正了这些代码]。本文有许多图表,如果你打印下来会看得更清楚。TAB字符设置为8个字符长度。本文的全部代码放于pmtut.asm文件中。如果你打算在其他的编译器中运行pmtut.asm,比如masm,[译者:此处删去关于如何从TASM2.01移植到MASM方法介绍。因为,我已经移植好了,下面的程序都是能在masm6.11中编译运行通过的,并且这段有些复杂,我翻译起来很困难:]我觉得应该把文章分为几个文件,每一个不同的主题对应一个文件(比如使用DPMI,VCPI,XMS和RAW进行模式切换,异常处理等等)。这样以来便于阅读。你认为如何呢?[第一章完]注释1:原文是Fidoaddress。Fido是一种专用电子公告牌,类似于今天BBS,更像今天的论坛1988年左右很火:)Fidoaddress是这个的标识。可以在下面的网址得到更多介绍~net653/fidonet.htm第二章开始好的,现在开始了。这篇文章就是关于保护模式...首先要了解进入保护模式所需。这里需要注意的地方,第一点就是需要检查你的处理器是否支持。进入保护模式需要80386机器以上的处理器。通过检测标志寄存器可以知道,标志位12-14位是特权级标志位,所以只需要检测这些位是否可改写就可以了(8086/8088,80186不使用这些位,并默认为0,80286开始有这些位,但是只有在保护模式下才可以修改[注2]。dos不能在保护模式下运行,所以即使是80286处理器,在实模式下也是不能修改的);checksfora386.modelTiny.datano386edb'Sorry,atleasta80386isneeded!',13,10,'$'yes386updb'Ok,itcanenterPM!',13,10,13,10,'$'.code.startuppushf;saveflagsxorah,ah;clearhighbytepushax;pushAXontothestackpopf;popthisvalueintotheflagregisterpushf;pushflagsontothestackpopax;...andgetflagsintoAXandah,0f0h;trytosetthehighnibblecmpah,0f0h;thehighnibbleisnever0f0honajeno386;80386!movah,70h;nowtrytosetNTandIOPLpushaxpopfpushfpopaxandah,70h;iftheycouldn'tbemodified,therejzno386;isno80386installedpopf;restoretheflagsmovdx,offsetyes386upmovah,09hint21hmovax,4c00hint21hno386:;ifthereisn'ta80386,putamsgmovdx,offsetno386e;andexitjmperr16exit;exitswithamsg;In:DS:DX-pointertomsgerr16exit:movah,9;selectDOS'printstringfunctionint21h;doitmovax,4cffh;exitwith0ffhasexitcodeint21h;goodbye....exitend[已经按照masm6.11修正并验证,注意,要编译为com文件]通过上面这段程序,我们可以很容易的检测出是否为80386或者更高的处理器。下面要做的第二件事情是检查程序运行的环境。扩展内存管理程序,诸如EMM386,QEMM等等,是通过切换到v86模式下提供服务的。我们的程序只能在实模式(REALMODE)下工作,我们需要另外的程序来检测当前所处状态。区分实模式和v86模式的方法是检测控制寄存器CR0的第0位[注3],为0说明在实模式下,否则不是。;checksifwearerunninginRealMode.modeltiny.datanrmedb'YouarecurrentlyrunninginV86mode!',13,10,'$'yesrealdb'YouarecurrentlyrunninginRealmode!',13,10,'$'.386.code.startupmoveax,cr0;getCR0toEAXandal,1;checkifPMbitissetjnznot_real_mode;yes,itis,soexit;no,itisn't,showmessagemovah,09hmovdx,offsetyesrealint21hmovax,4c00hint21hnot_real_mode:movah,09hmovdx,offsetnrmeint21h.exitend[我做了一些修改,masm6.11可以编译运行。我在FREEDOS中运行,发现启动加载HIMEM.SYS后仍然是实模式,而加载EMM386之后就处于V86MODE了]上面的测试完成后,我们可以确信能进入保护模式。DMPI和VCPI(甚至BIOS)都可以用来切换实模式和保护模式,使用这样功能调用很简单,留给你练习(昀后面描述了部分接口)。注意,上面的程序中我使用了moveax,cr0JerzyTarasiuk指出在保护模式下是不允许这样做的,特别是在286的机器上。如果你的机器不容许,请使用smswax这条指令只在386及其以上才有,它在任何环境下都可以使用。与之匹配,你也可以用lmswax来代替movcr0,ax切换到保护模式.需要注意的是你只能使用lmsw进入保护模式。但不能用这条指令无法令从保护模式退出。如果想退出的话只有关机或者重启[译者这篇文章只讲进入没讲退出]。就先说这么多,对于初学者这个问题过于复杂:)[第二章完]注释2:IA-32Intel®ArchitectureSoftwareDeveloper’sManualVolume1:BasicArchitecture3-13页Figure3-7.EFLAGSRegister注释3:IA-32Intel®ArchitectureSoftwareDeveloper’sManualVolume3:SystemProgrammingGuide2-12页Figure2-5.ControlRegisters下面的表来自2-13页PGPaging(bit31ofCR0).Enablespagingwhenset;disablespagingwhenclear.Whenpagingisdisabled,alllinearaddressesaretreatedasphysicaladdresses.ThePGflaghasnoeffectifthePEflag(bit0ofregisterCR0)isnotalsoset;infact,settingthePGflagwhenthePEflagisclearcausesageneral-protectionexception(#GP)tobegenerated.SeeSection3.6.,“Paging(VirtualMemory)Overview”,foradetaileddescriptionoftheprocessor’spagingmechanism.CDCacheDisable(bit30ofCR0).WhentheCDandNWflagsareclear,cachingofmemorylocationsforthewholeofphysicalmemoryintheprocessor’sinternal(andexternal)cachesisenabled.WhentheCDflagisset,cachingisrestrictedasdescribedinTable10-5.Topreventtheprocessorfromaccessingandupdatingitscaches,theCDflagmustbesetandthecachesmustbeinvalidatedsothatnocachehitscanoccur(seeSection10.5.3.,“PreventingCaching”).SeeSection10.5.,“CacheControl”,foradetaileddescriptionoftheadditionalrestrictionsthatcanbeplacedonthecachingofse