管理员
|
阅读:3754回复:0
[系统教程]汇编语言的艺术(组合语言的艺术)--基本认识(2)
楼主#
更多
发布于:2011-10-11 19:46
![](http://www.atcpu.com/themes/extres/ithread/images/3A.gif) | | ![](http://www.atcpu.com/themes/extres/ithread/images/3C.gif) | ![](http://www.atcpu.com/themes/extres/ithread/images/3.gif) | 四、对指令的认识
指令就是「指挥」、「命令」,用以控制电脑,一步一步地实现程式的计划。 组合语言的格式为: ( 下行中凡标“[ ] ”者,表有些指令可省略 ) [前置元] 指令 [目的操作元,源始操作元] 1,「前置元」:以下诸例即为前置元的用法。 11段名:表后面的操作元应属于此临时前置段。如: MOV AX,CS:BUF1 12定义:表示其后缓冲器的临时定义。BYTE PTR表示以一个字元定义的资料; WORD PTR表双字元资料。 不论缓冲器的原定义为何,凡有前置元者,皆以临 时定义为准,如: ADD BYTE PTR BUF1,CL 前置元除了定义缓冲器长度外,亦可表示距离, JMP SHORT ABCD
2,指令: 11使用方法: 1-1 暂存器到暂存器,但限长度相同者。 MOV AH,BL ; 为字元 XCHG AX,BX ; 为二字元 1-2 暂存器到缓冲器,或缓冲器到暂存器。 OR BUF1,AX ; BUF1为缓冲器,WORD ADD CL,BYTE PTR BUF1 1-3 数值与暂存器或缓冲器之间。 TEST DI,8000H AND SI,0FFH SUB BYTE PTR BUF1,3 ★数值绝不可作为「目的」操作元 1-4 将记忆区的地址放在暂存器中,以传送该地址的内容,或传送变数以便间接调用资料。本法限用于源存器(SI)、终存器(DI)、栈用器(BP)及兼用器(BX)。如: MOV AL,BYTE PTR [DI] XOR [BP],DL MOV AX,[DI][SI] MOV AX,BUF1[DI] JMP LAB1[BX] 1-5 执行指令本身,不需源始或目的操作元。 PUSH CS POP DS CALL ABCD JMP ABCD CLI STD LAHF RET 1-6 执行计数者。 LOOP ABCD REP MOVSB SAL DL,CL ROR AX,1 DEC BX 1-7 暂存器专用指令。 OUT DX,AL MUL BUF1 DIV CX STOSB LODSW 1-8 条件执行者。 JNZ ABCD JA ABCD JCXZ ABCD INT 10H IRET 12应用功能可分为下列八项: 2-1 资料转移:1-1,1-2,1-3,1-4皆有可能。 2-2 旗号控制:1-5 涉及旗号者。 2-3 段址处理:1-1,1-2 项可能。 2-4 数学计算:视指令而定,上述各项皆可。 2-5 字串处理:1-6,1-7 项功能。 2-6 控制转换:1-5。 2-7 条件执行:1-8。 2-8 中断处理:1-8。
3,操作元:可分成暂存器、缓冲器及数值(Immediate Data)。其书写方式与习惯的由前到后正好相反,使用时要小心,其余细节请参看有关组合语言手册。
第二节 工作环境
一、系统空间
IBM PC的记忆区定址,是采用倒装方式 (Big Endian) ,即定址值系由大到小,不同于一般由小而大(Little Endian) 的定址常识。 不论当初如此设计的目的何在,这种与人的习惯相反的观念,给写作组合语言者带来极大的困扰。不仅初学者常莫明其妙,连我个人多年来一直与图形处理为伍,都感到汗颜。每次在处理图形时,一定要将原图画在纸上,对照参详,才能瞭解是怎么回事。 举例说,有个图形值在AX中,要写进 DI 所指记忆区位置中,写完以后,AX要向右移一位再继续写,直到CX=0。 这是一个非常简单,而且经常用到的动作,可是在使用「倒装定址」时,麻烦就来了。 假设AX值为4567H ,DI指向记忆区2000H ,倒装的放法,是先将AL的值放进2000H 的记忆单位中,再将AH放进2001H 的记忆单位里。如果从由小到大的定址观点来看,这就等于是在2000H 中放了一个十六位元的值6745H 。 这倒不打紧,因为再从记忆位址2000H 中放回 AX 时,仍然成为4567H 。问题是在作图时,一旦4567H 变成了6745H ,图形就左右颠倒了。补救的方法,是在放进记忆区之前,先将AH及AL交换,放完以后,再重新交换回来。说来不算大事,可是白白浪费了两个指令的时间及空间。对速度极关紧要的画图显示而言,要画几万个点,所累积的时间就不可小观了。 除此之外,在写程式时,对图形的效应要能掌握,才会有良好的成果,像这样每次转来转去,头都昏了,自然而然就失去了耐性。 现在,80386 cpu 问世了,且不谈效果,读者可以试想,把32位元的 12345678H转换成 78563412H要多少道手续? 这种痛苦的手续,也是美国人不愿意用组合语言的理由之一。在高阶语言中,有编译器代劳,问题好像不大。但对效率的要求而言,就得不偿失了。图形功能是当今及未来电脑的主流之一,由于当初设计者没有远见,导致无穷的后患。 问题尚不止于此,IBM PC/AT 的系统空间,在定址的理论上,可以有 1MB(暂时不必考虑记忆扩充及EMS 等问题),然而真正能提供作为程式执行的空间,却不足 600KB。
我们且看其系统空间的安排: 0000H 段 0000H-007FH 计 128字元,为32个基本中断。 0008H 段 0000H-0380H 计 896字元,供系统管理中断。 0040H 段 0000H-00FFH 计 256字元,为基本程式资料。 0054H 段 0000H-9C00H 约 34K字元,DOS 程式占用。 唯有在 00E1H段-09000H段的前半是使用者可以控制的空间,其后,又被系统占用: 09000H段由0A000H附近直到0FFFFH,为DOS 所用。 0A000H段,为 VGA图形显示区。 0A800H段,为 EGA图形显示区。 0B000H段,为文字态缓冲区,萤幕处理器6845自动管理。 0B800H段,为图形态显示区,萤幕处理器6845自动管理。 0C000H段,至0D000H段,各机种不定,供 EMS扩展记忆。 0C800H段,为唯读记忆体,其内为硬盘控制程式。 0E000H段,1MB 的主机此处为 RAM,否则此段不能使用。 0F000H段,为唯读记忆区,其内为基本输出/入程式。
由上可知,整个系统的规划不尽理想,尤其受限于8088的CPU 原先错误的设计理念(段暂存器现为定址的16倍,即每进一,相当于地址增加16。在最初,如果不考虑与8080兼容,原可轻易地定为 256或更高倍。)所以,当要扩充记忆容量时,便产生了 EMS这种无可奈何的高科技畸形儿。二、周边设备
所谓周边设备,率指须透过系统的输出/入汇流埠(I/O Port),及其管理程式所控制的外部各种设置。 在此定义下,键盘就是一种周边设备,除此之外,萤幕显示器、印表机、磁盘机等,均属周边设备。显然,程式师必须瞭解每一种周边设备的性质,否则无法下手。 由于周边设备种类繁多,且各有其使用规格,可以说毫无技巧可言,故本书不拟一一介绍。要之,把各种设备所定义的规格条件,抄录在记事簿中,以便随时查阅。 此外,为求程式能有效地应用于各种不同规格的周边设备上,千万不可在应用程式中统一处理,最好定妥各种介面,作为附属程式,由使用者自行设定。 这样规划的第一个原因,是无人能预知到底未来需要多少种不同的设备,挂一漏万,以后程式增改不易,可能导致功能不足,或程式松散的后果。 第二个原因在,使用者经常使用的设备是固定不变的,将一些永远用不到的程式放在一起,是无谓地浪费空间。 第三个原因为技术虽在进步,程式应用观念则难以改变,主导程式与周边设备之介面程式不应纠结在一起。一个没有渣滓、精心雕琢的程式才有永恒的价值。终有一天,当电脑技术成熟时,原应用程式无需改动,仅将处理周边设备的附属程式换成新的即可。 这就是生命,就是新陈代谢,有了这些认识,才能理解组合语言的精义。
三、系统程式
在 ibm pc/at系统中,只有两种系统程式,一是磁盘作业系统程式 (ms-dos 或 pc-dos ),负责系统启动、记忆区管理以及部份输出/入处理等工作。此系统程式原贮存在系统磁盘中,开机时才调入系统中,所以容易修改。由最初推出的版本1.0 ,到现在已是4.01,其功能还在不断地改进中。 另一种为基本中断服务程式(Bios),贮存在唯读记忆体中,除非机种易动,否则永远不会改变。基本中断程式的主要功能为便利程式师,把所有的周边设备所需要的参数,统一由暂存器代为传输。程式师可按照规定,把正确的值,放到规定的暂存器中,基本中断便会优先执行。 这两种系统程式,程式师必须熟悉,至少,应知道何种功能要用哪一个中断。 这两种系统程式,都因瞻前顾后,速度不够理想。因之有些程式师,根本不用这些中断,自行控制输出/入埠。这种做法确实能提高速度,自由控制。而相对的,程式的通用性也减低了。是否值得,设计前应先考虑清楚。 此外,这两种中断程式有些相互重复之处,如键盘输入及萤幕输出等,经常令人不知如何选用。有人建议用磁盘作业的中断,我则认为该用基本中断。 因为系统容许程式改变基本中断的入口值,所有利用基本中断的程式,都可修改入口,以增加其应用功能。磁盘作业系统则不然,虽然该程式在磁盘上,且在不断地改进中,但在改进之时,又必须兼顾过去的客户。时间一久,问题就发生了。且改进越大,越显得过去的作业方式落伍,兼容就是保留过去渣滓的代名词。兼容性越高,包袱就越重,空间浪费越大。 建筑在这种基础上的程式,必须冒种风险:是否有一天,磁盘作业系统会面临运转困难或遭解体的厄运?O/S2的问世已经表明了,此系统的大限业已到来。 基本中断可以改变,意思是说,除了一部份BIOS空间的浪费无可避免外,在PC系列中,系统中断的观念不会再改变。只要程式师能把握基本中断程式的技巧,则不论未来的系统变化到任何地步,一个具有实用价值的程式,理论上其生命期应该是很长的。
四、配备程式
配备程式指的是一些非必要的基本程式,只因为特殊需要而调用。通常,它是由某些系统提供,配备给某些程式的。 配备程式包括各种计算的函数及绘图公式,特殊处理用的lib.等,在某些情况下,也可以将之视为环境,例如视窗管理ms-dos window,记忆扩充装置 ems等。 配备程式的产生,证明了电脑软体发展的迂回历程,同时也表示出软体的灵活性。在我个人的观念中,配备程式如果能有一定的设计方式,有统一的规格,很可能在大量的、不断发展下,成为一个个「公用模组」,并可专门提供模组,以供用户应用,使得软件的制作变得轻而易举。 写作或应用这些程式,别无其他法门,唯有熟记于胸,才能得心应手。
五、公用模组
模组应是未来电脑软件发展的主流,每一类模组的功能,代表了各行各业的经验及诀窍。使用者无需瞭解模组的制作技巧,只要知道如何调用,就可以完成工作。 目前尚无厂商提供「公用模组」,但是随着观念的拓广,一旦有了理论,有人先行一步,这种潮流即将形成。我们即将推出的“聚珍整合模组”,第一阶段尚限于程式师使用,再下一步,当客户直接调用的介面完成后,程式的发展方向又将改弦易辙了。 第三节 处理对象
一、数据资料
数据资料率指可以输入、处理及计算的二进位资料,在工作过程中,安全性为第一考虑因素,同时要兼顾精确以及完整性。此类资料一般说来数量都相当大,要妥善规划资料长度,否则存贮空间会成为执行程式时的主要课题。 写作此类程式时,各种进位制的转换,显示区的定位,计算公式的处理等都应该作为子程式,以便任意调用。 而真正关键问题却在于:数据的极限是否能够明确得知,在有限的范围中,绝对可以设计一种「结构化」的规格,符合效率的需求。否则也应根据其规则性,配合程式的特性,有效地加以处理。
二、文字资料
文字资料多为字符态,拼音文字所应该注意的是,字与字间的空间调整,齐头、齐尾、齐中等变化,行末断字的规定,以及字体、字形、字号等。 中文尚有输入码、内码等处理问题。原则上,如果要考虑中、英文兼容,则应注意萤幕上的字形显示与字码记忆区的位置,应占相同的比例。 目前,由于英文字、码不分,皆占一字元,萤幕上标准格式为25行80字,即采用所谓「文字状态」。而中文字形至少要有16x 16点阵,且需用图形方式(也有采用文字态,再加特殊硬体者,但成本偏高,有碍中文电脑未来发展)。因此,当采用640x 400或近似规格时,中文字形与英文之比,约为2:1。 在此基础上,以二字元为中文的内码长度,是最常用的结构。但是随着技术及观念的进步,有些英文系统已在使用二字元码,是则,中文有使用四字元的必要。 从另一方面来看,大陆所用的「国标码」,系抄自日本五十年代的 JIS CODE -日本工业标准,最多仅能容纳8836个符号,其中「汉字」尚不足八千。而国标码更为精简,收字6763个。中文源自中国,现在却借镜东洋的「工业标准」,且摇身一变,竟成为十余亿人口的「国家标准」,真可谓每下愈况。无独有偶,台湾也有所谓的标准,BIG-5 的13,053字,虽然是国标码的两倍,二者终究是五十与百步之差而已。 为什么要订定这种「辱及先祖」的文字标准呢?谁又够资格订定中文标准呢?从事电脑工作者不过是些「技术专家」,连电脑这一行所有的技术尚未必精通,更何况隔行如隔山,竟然捞过界,捞到文字界这个相离十万八千里的领域去了。 文字是人类思想、文化的载具,先贤先圣们殚精竭虑所创造的文字,就是用来传达他们对宇宙、人生的认知。我们后代子孙不肖,不能领悟其微言大义倒也罢了。对电脑技术瞭解不足,没有能力令电脑应用中文,这也可以理解。但自以为是,依权仗势,妄想偷天换日,仅用少数认识的文字,定为整个国家的「文字标准」,并强制国人接受,这种颟顸就难以令人苟同了。 在运用中文时,由于各家发展的系统观念不一,有的甚至违法盗袭国外软件,为了兼容起见,必须「削足适履」。原则上,中文内码将第一字元中第八位位元设为一,得以与「美国工业标准码」的ASCII (American Standard Code for Infor-mation interchange)有别。 文字资料处理上最重要的工作,是排序的技术问题,国标码仅六千多字,却分为二集,把常用字放在前集,次常用字在后部。但是这种顺序与使用人的观念毫无关连。除了统计这种使用频率的学者专家外,不可能有几个人理解何字是常用字,何字不是。 于是,当我们要利用电脑的高速效率,将输入的中文加以排序整理时,国标码完全起不了作用。也就是说,编码原为提高效率,而我们的编码只为了编码,与效率毫不相干。 唯一的补救办法是,再建一个排序表,与国标码一一对照使用。 高科技界因为利润高、地位高,故而高论、高见特多,只是动起手来就难免「眼高手低」,再不然则是「高论调、低效率」。 相信人人都有查字典或电话簿的经验,对用英文来说,是轻而易举,中文则麻烦多多。国人只知抱残守缺,自卑自怜,而不求瞭解其因果原理。一般人如此倒也罢了,高科技界倘如此,就有点说不通,甚至令人怀疑是否别具用心。 英文所以方便无他,因其字母具有直接索引的功能!中文则有前人订定了一套「部首、笔画」的索引观念。这在过去资讯不发达的时代,的确是个创见,也足敷应用,但是现代与字母的直接索引相较,在效率上究竟差了一大截。 也有人认为,我们要维护中华文化,就应该死抱着古人所定的索引观念。这种说法只有一点不足,就是忘了把大汉衣冠也穿得整整齐齐,甚至用文房四宝取代现代化事务工具! 麻烦的是,「部首、笔画」是两种不同的索引观念,当没有时间因素介入时,孰先孰后关系不大。可是用到电脑上,就必须定先后次序,否则碍难执行。 对姓氏笔画少的人,当然主张「笔画」优先,姓氏部首明显的,则主张先排「部首」。这点不难理解,出席一个重要的庆典,或在报上亮相,人数一多,排名先后所涉及的利益,至关重大,不能不争!问题在于,除了私利外,部首笔画这种没有效率的索引观念,还有什么实用的价值?如果一定要保存,作为一种特例,当然可以。可是电脑所追求的是效率,每个中文的部首和笔画,都需要建对照表,才能应用,字集越大,空间需求越大,时间消耗也越长。 这还不说,索引不仅是提供给电脑用的,人更需要。仅以查电话本为例,「张伟雄」这个名字,我们凭什么知道其前后的「定位」关系呢?表面上看来,只要查三次部首及其笔画、以及数三次这三个字的笔画。而事实上,在查找的过程中,每遇到一个名字,都要重覆前述的手续,才能加以比较。 有人振振有词说,有些字一眼看过去就知道是几画!至少我个人没有这种本事。而且根据统计,中文平均以十四画的居多,由九至十八画,就很难靠视觉分辨!再若人名一多,视觉就很容易疲劳。 又有人说话了,现在是给电脑排序,与人不相干!殊不知字母排序可以立即执行,而部首、笔画排序要多作三至六次动作,兹以先部首后笔画为例:
1,查本字之部首序值。 2,查对照字之部首序值。 3,比较两者之大小,决定是否需要再比。 4,再比时,查本字笔画数。 5,查对照字之笔画数。 6,比较两者之大小,以决定序位。 把这些步骤写成程式,以中文两个字元的内码计,(意思是说中文收字在两万以下)如果用对照表的方式,空间当在64KB以上,速度则较英文慢约50倍。再若采用公式计算,空间或能节省,但速度将慢上千、百倍之多。 这还是指两万字以下的情况,若采用汉字全字集,后果将不堪设想。所以「专家」们一致认为,为了效率,字收得越少越好!怎样才能算是真正的「中文电脑」?我十多年前所面对的「敌人」,是主张将中文字埋葬掉。这种人不难对付,因为到底他们还是中国人,在民族大义的旗帜下,多多少少心中也存着乐见中文电脑成功的意愿。所不同的,只是他们不相信有此可能罢。 现今的「敌人」则顽强得多,他们同样喊着民族大义的口号,又是公认的中文电脑「专家」。更可怕的,目前使用中文电脑的人,不见得对中国文化有什么明确的认识,有个工具列印一些文件,就相当知足了。于是,这些客户也在其主观的立场,认定目前这种「市场占有率高」的半调子,就是「中文电脑」的标准! 是吗?如果中文字有六、七万字,而目前只能用几千、甚至一万多字,那么其他的字呢?算不算是中文,如果算,为什么「中文电脑」中没有?这种电脑能说是「中文电脑」吗? 有人又说了,没有关系,以后再说。怎么说呢?有一种方法,是将文字「分集」,分成:常用字、次常用字、次次常用字、罕用字、罕罕用字等等。且不管是哪位学者有这么大的学问去「分集」,我所知道的只是用这种方法,人无从记忆,中文排序的难度又一倍一倍地加了上去。也难怪当初有人认为中文不科学,这不是明证吗? 其实,中文排序根本没有问题,我们利用仓颉字母作为索引,效率与英文相等,而且收字可以高达千万个。至于记忆空间,一个字元都不需要。更有利的是,用作字典、电话簿等的索引,一查即得。 内码是各个系统、根据其不同的需求、所订的一种资料形式,没有任何理由强制规定。当然,如果内码种类多了,姑不论其编码的好坏,各个中文系统之间,自然会形成难以沟通的障碍。于是有必要建立一种「交换码」,供不同系统的内码,统一交换应用。 这种交换码才有标准化的必要,而且订定之时,应该谨慎从事,要能容纳所有各家系统所收的字,否则无从交换。 不论是哪种码,必然会有其特殊规定,在写作之前,程式师一定要设法找到该系统所用的「码表」,否则无法处理。
三、图形资料
在电脑图形资料的处理方面,目前只有点阵及向量两种形式,前者即二进位资料 (Digital Data) ,后者则是绘图用的公式值。实际上,还有所谓「概念资料」的形式,将视觉效应经过分析后,整理成为人能够理解的「概念」。这种概念资料非常精简,便于贮存,取出后,再通过「概念作图」的过程,还原成为图形。 一个优秀的画家,必然有这种概念作图的能力,只要把画家的经验写成程式,将其记忆的特征设计为资料,电脑必将忠实地执行,而且每次都画得一模一样。 如果是处理二进位点阵资料,不外乎是压缩、还原、截取及综合等几种简单功能。绘图向量值则比较复杂,涉及计算、调整、变数、层次等多种技巧。 简单地说,绘图资料所考虑的,比文字资料难度高,要想得到理想的效率,最重要的应是资料结构的定义,其次是层次的安排,以及特征性质的描述等。此外,输入变数处理涉及人的应用方式,除了专业人员外,多数人尚未能适应这种新的绘图观念,经验的不足,以致迄今尚未制作出理想的程式来。 概念绘图必将成为未来的主流,它不仅符合人类的认知习惯,且易于应用。只要概念资料建得周全、完整,略为改变其中一些概念元素,就能得到各种结果。
四、概念资料
人类系以概念进行思考,并透过概念来认识外界。所以,对人而言,最有效的应用方式,就是人已经熟知的概念。 概念并不是语言,而是组成语言的最基本因素。每一个人对外在世界的认知,都是独一无二的,由于人类生存在群体空间里,需要经常彼此交换经验,于是利用听觉效应表达概念,便产生了语言;利用视觉符号,则产生了文字。 前述的图象概念资料属于「具象」资料,除了具象以外,还有抽象的,包括主观的感受、认知、欲望等等,因与主题无关,这里不加讨论。总之,这些概念资料的结构,在电脑中必然是二进制的形式,只是因每一个设计者观点的不同,性能有所区别罢了。 直到如今,尚未见到实际应用概念资料的程式,但是它将成为电脑的基本结构,却是指日可待的。 作为程式师,天天与电脑为伍,不能不知道电脑未来的趋势,更不能不多加努力,掌握技术发展的机先。正因为概念资料尚未定形,人人都有相同的机会,做一个开创时代的先河。否则,等到大局底定时,只有在后面苦苦追赶,由不得己了。
五、综合资料
功能较强的程式,很少仅具有单一的资料。尤其是「整合软件」越来越受到重视,各种资料最终都将综合在一起。 综合资料有两种意义,一是人所认识的输入资料,一是电脑贮存的处理资料。 输入资料又可分指令及字符两种,在传统的观念里,不将指令视为资料,因为指令一旦执行以后,即不再发生作用。可是,在桌上型排版软件广泛流行以后,为了控制版面,必须将相关指令随资料同时贮存起来。而排版已经成为电脑重要的功能之一,所以在未来的发展上,输入资料必须考虑到指令。 在整合观念中,输入资料应有统一的规定,亦即不论是何种性质的软件,其键盘的应用、字符的定义等,都应该有全面的考虑。 关于资料内容,也有 ASCII字符及「世界字符」之争,对早期的英文系统而言,其他文字无关紧要,所以没有适当的「世界字码」可供应用。然而,资讯时代究竟不是英文使用国家的专利,在各国觉醒之际,都憬悟到字码的重要。不论 ISO国际组织如何面对问题,我个人不相信世界文字在其保留的、极为有限的「编码平面」上,能够发挥多大的效益。充其量,可供一段时间内、某些商业上的应用而已。 我认为真正的资讯标准,将是以各国文字为根本的自然语言,而目前最理想的方式,则为多字元的字码方案。拼音文字系统以二字元为宜,除了可以同时应用世界各国文字以外,并且符合当前微电脑的发展趋势。 在中文系统上,我们采用四字元的「自然码」,即将仓颉输入码压缩的方案。如此,我们可以使用上千万个中文字,有人会说没有人需要那么多字,但事实上有谁能预料呢?当初仓颉造字时,相信不会超过一千,如果他武断地订定「标准」限制后人用字,很难想像我们的民族还会有什么文化? 台湾曾有专家对我这种意见,表示是「不合乎潮流,注定要失败」,然而到底是谁不合潮流呢?四字元的微电脑已经到来了,而且被公认为今后的主流。在四字元的硬体结构上,自以一次读取四字元、其次为二字元最为有效。所以这些观念已经落伍的专家,还是去捞些钞票,把研究发展的工作,交给够资格的人去做吧!
第四节 指令应用
组合语言可以说是未经整理的、原始的电脑语言,读者们大可下一番功夫,找出其应用的规则,以发挥最高的效率。在下面,我仅就个人的经验,提供一些浅见,以供切磋研讨。 要写好程式,首先应熟记8088指令的时钟脉冲(Clock )及指令长度,一般组合语言手册中,都详列了与各指令相关的资料。「工欲善其事,必先利其器」,此之谓也。 本节所讨论的,是一般程式师容易忽略的细节,所有的例子都是从我所看过的一些程式中摘录下来的。看来没什么大了不起,可是程式的效率,受到这些小地方的影响很大。更重要的是,任何一个人,只要有「小事不做,小善不为」的习惯,我敢断言,这个人不会有什么大成就! 我最近才查到 Effective Address (EA) 的时钟值,我觉得没有必要死记。原则上,以暂存器为变数,做间接定址时为5个时钟,用直接定址则为6个;若用了两组变数,则为7至9个,三组则为11或12个。 为了便于叙述,下面以“T”表「时钟脉冲」; “B”表字元。其中 时钟脉冲T = 1 / 振荡频率
一、避免浪费速度及空间
组合语言的效率建立在指令的运用上,如果不用心体会下列指令的有效用法,组合语言的优点就难以发挥。 1, CALL ABCD RET 这种写法,是没有用心的结果,共用了 4B,23T+20T,完全相同的功能,如: JMP ABCD 或 JMP SHORT ABCD 却只要 2-3B,15T。 此外,上述的CALL XXXX 是调用子程式的格式,在直觉认知上,与JMP XXXX完全不同。对整体设计而言,是不可原谅的错误,侦错的时候,也很难掌握全盘的理念。 尤其是在精简程式的时候,很可能会遇到 ABCD 这个子程式完全独立,是则把这段程式直接移到 ABCD 前,不仅能节省空间,而且使程式具有连贯性,易读易用。
2, MOV AX,0 同样,这条指令要 3B,4T,如果用: SUB AX,AX 或 XOR AX,AX 只要 2B,3T, 唯一要注意的是,后者会影响旗号,所以不要用在有旗号判断的指令前面。 在程式写作中,经常需要将暂存器或缓冲器清为0,有效的方法,是使某暂存器保持为0,以便随时应用。 因为,MOV [暂存器],[暂存器] 只要 2B,2T, 即使是清缓冲器,也比直接填0为佳。 只是,如何令暂存器保持0,则要下一番功夫了。 还有一种情况,就是在一回路中,每次都需要将 AH 清0,此时对速度要求很严,有一个指令 CBW 原为将一 个字元转换为双字元,只需 1B,2T 最有效率。可是应该注意,此时 AL 必须小于 80H,否则 AH 将成为负数。 3, ADD AX,AX 需要 2B,3T不如用: SHL AX,1 只要2B,2T。
4, MOV AX,4 除非这时 AH 必为0,否则,应该用: MOV AL,4 这样会少一个字元。
5, MOV AL,46H MOV AH,0FFH 为什么不写成: MOV AX,0FF46H 不仅省了一个字元,四个时钟,而且少打几个字母!
6, CMP CX,0 需要 4B,4T, 但若用: OR CX,CX 完全相同的功能,但只要 2B,3T。再若用: JCXZ XXXX 则一条指令可以替代两条,时空都省。不幸这条指令限用于CX ,对其他暂器无效。 7, SUB BX,1 这更不能原谅,4B,4T无端浪费。 DEC BX 现成的指令,1B,2T为何不用? 如果是 SUB BL,1 也应该考虑此时 BH 的情况,若可以用 DEC BX 取代,且不影响后果,亦不妨用之。
8, MOV AX,[SI] INC SI INC SI 这该挨骂了,一定是没有记熟指令,全部共4B,21T。 LODSW 正是为这个目的设计,却只要 1B,16T。
9, MOV CX,8 MUL CX 写这段程式之时应先养成习惯,每遇到乘、除法,就该打一下算盘。因为它们太浪费时间。8位元的要七十多个时钟,16位元则要一百多。所以若有可能,尽量设法用简单的指令取代。 SHL AX,1 SHL AX,1 SHL AX,1 原来要 5B,137T,现在只要 6B,6T。如果CX能够动用的话,则写成: MOV CL,3 SHL AX,CL 这样更佳,而且CL之值越大越有利。用CL作为计数专 用暂存器,不仅节省空间,且因指令系在 CPU中执行,速 度也快。 可是究竟快了多少? 我们做了些测试,以 SHL为例,在10MHZ 频率的机器上,作了3072 ×14270次,所测得时间为: 指 令 :SHL AX,CL SHL AX,n CL = 0 , 23 秒 n = 0 , 无效 CL = 1 , 27 秒 n = 1 , 14 秒 CL = 2 , 32 秒 n = 2 , 28 秒 CL = 3 , 36 秒 n = 3 , 42 秒 CL = 4 , 40 秒 n = 4 , 56 秒 CL = 5 , 44 秒 n = 5 , 71 秒 CL = 6 , 49 秒 n = 6 , 85 秒 CL = 7 , 54 秒 n = 7 , 99 秒 由此可知,用CL在大于2时即较分别执行有效。 此外,亦可利用回路做加减法,但要算算值不值得,且应注意是否有调整余数的需要。
10, MOV WORD PTR BUF1,0 MOV WORD PTR BUF2,0 MOV WORD PTR BUF3,0 MOV BYTE PTR BUF4,0 .. 我见过太多这种程式,一见就无名火起! 在程式中,最好经常保留一个暂存器为0,以便应付这种情况。即使没有,也要设法使一暂存器为0,以节省时、空。 SUB AX,AX MOV BUF1,AX MOV BUF2,AX MOV BUF3,AX MOV BUF4,AL
14B,59T取代了 24B,76T,当然值得。只是,还是不 如事先有组织,考虑清楚各个缓冲器间的应用关系。以前面举的例来说,假定各缓冲器内数字,即为其实际位置关系,则可以写成: MOV CX,3 如已知 CH 为0,则用: MOV CL,3 SUB AX,AX MOV DI,OFFSET BUF1 REP STOSW STOSB 这段程式越长越占便宜,现在用10B,37T,一样划算。
11,子程式之连续调用: CALL ABCD CALL EFGH 如果 ABCD,EFGH 都是子程式,且调用的次数甚多,则上述调用的方式就有待商榷了。因为连续两次调用,不仅时间上不划算,空间也浪费。 若ABCD一定与EFGH连用,应将ABCD放在EFGH之前: ABCD: .. EFGH: .. 像这样,只要调用ABCD就够了,但这种情形多半是程式师的疏忽所致,如两个子程式必需独立使用,而上述连续调用的机会超过两次以上,则应该改为: CALL ABCDEF 而ABCDEF则应为: ABCDEF: CALL ABCD EFGH: .. 这样的写法速度不会变慢,而空间的节省则与调用的次数成正比。
12,常有些程式,当从缓冲器中取资料时,必须将暂存器高位置为0。如: SUB AH,AH MOV AL,BUFFER 这时应该将 BUFFER 先设为: BUFFER DB ?,0 然后用: MOV AX,WORD PTR BUFFER 如此,不但速度快了,空间也省了。
13,有时看来多了一个指令,但因为指令的特性,反而更为精简。如: OR ES:[DI],BH OR ES:[DI+1],BL 这样需要8B,32T,如果改用下面的指令: XCHG BL,BH OR ES:[DI],BX XCHG BH,BL 则需7B,28T。
14,PUSH 及 POP 是保存暂存器原值的指令,都只需一个字元,但却很费时间。 PUSH 占 15T,POP 占12T,除非不得已,不可随便使用。有时由于子程式说明不清楚,程式师为了安全,又懒得检查,便把暂存器统统堆在堆栈上。尤其是在系统程式或子程式中,经常有到堆栈上堆、取的动作。实际上,花点功夫,把暂存器应用查清楚,就可以增进不少效率。 要知道,系统程式及某些子程式常常应用,有关速度的效率甚大,如果掉以轻心,就是不负责任! 保存原值的方法很多,其中较有效率的是放到一些不用的暂存器里。以我的经验,堆栈器用途最少,正好用作临时仓库。但最好的办法,还是把程式中暂存器的应用安排得合情合理,不要浪费,以免堆得太多。 还有一种方法,是在该子程式中,不用堆栈的手续,但另设一个入口,先将暂存器堆起,再来调用不用堆栈的子程式。这两个不同的入口,可以分别提供给希望快速处理,或需要保留暂存器原值者调用。 当然,更简单有效的方法,则是说明本段程式中某些暂存器将被破坏,而由调用者自行保存之。(灯火互联网)
| | ![](http://www.atcpu.com/themes/extres/ithread/images/3G.gif) | | ![](http://www.atcpu.com/themes/extres/ithread/images/3I.gif) |
|