汇编语言的艺术(组合语言的艺术)--基本认识(1)

来源:岁月联盟 编辑:zhu 时间:2009-03-09

  第一节 应用工具

  一、对程式的认识

  写作程式不难,但要写出好程式却不容易。这就好像画图一样,人人都能画,而画出来的图却可能有天壤之别。

  想作一个好画家,首先要有观察及分析的能力,面对着杂乱的事物,先整理出头绪,找到主题。再在画布上勾出轮廓,这叫做「布局」。布局完毕,根据实际的环境,决定作图的先后「顺序」。顺序是一种层次观念,景物及色彩都有一定的层次,绝不可随意所之,想到哪里,画到哪里。

  观察考虑完毕,即开始准备,先将画笔、调色板等工具放妥,把要表现的主要色彩也调好。最后是选择适当的画笔,蘸上色彩,按照所观察的结果,涂在画布上。

  画图颇重风格,有些个人主义的艺术家,技巧并不精通,只因为时代潮流或历史条件,创造了某种独特的风格,就得以成名享利。一般的画家则不然,不论是「工笔」抑或「写意」画,全靠其技巧及素养,始能求生存。至于艺术大师,则首重风格,再加上素养、技巧,方可扬名立万,永垂不朽。

  最糟糕的画匠,既没有观察能力,更谈不上技巧和风格,除了照着别人的作品抄袭、模仿外,创造不出有价值的作品。若程式师也如此,只能照着别人的意思,填填指令,不过是个程式匠罢了。

  在观察分析之下,把欲表现的内容整理成为具体的步骤,用电脑术语来说,是为「程式分析」,相当于画画中的「布局」。再下去,便是「流程」制作,或是作画的顺序。将各种程式的层次安排妥当,才能开始写作程式,相当于开始作画。

  这些观念牵涉甚广,不是三言两语可以说完。本书仅以组合语言写作的训练为目的。如果读者能善用组合语言的各种技巧,又能充份认识所要完成的工作,至少可以满足「工笔画」的条件。对一个电脑程式而言,目前画「工笔画」的价值要比「写意」为高。

  下面,我们要以工笔画的立场,来理解组合语言的应用。对油画或水彩画而言,色料相当于程式用的「资料」,调色盘就是运用资料的「暂存器」,画笔等于「指令」,一切都准备妥当,所谓「作画」就是「写程式」。

  程式是由一系列的定义和指令组织成的可执行的程序,需由一种档案的形式(.asm)经过编译程式(masm.exe)的处理,将原始档转变为目的档(.obj),然后再将一个或数个目的档经过联结(link.exe)成为执行档(.exe),或者再用exe2bin.exe制成记忆限在64kb以下的命令档(.com)。

  程式师应熟悉上述过程中的每一细节,方能顺利完成程式写作。

  程式的写作方式本无定则,完全看需求及应用而定。可是正如一幅画,在布局时,程式师应该先有全部的观念,然后逐步实行。为了提高效率,这些步骤,有必要加以归类。结果就是所谓的模组。

  模组的良窳,决定了程式写作、修改及再应用的效能。在写作时要求理念一贯,连续进行。修改应方便灵活,不致错误丛生。而应用上功能要完整,可以独立调用。 

  根据上述条件,程式的结构大致上可分为:

  1,主程式:连贯性的处理过程,应该一次考虑清楚,细节暂  时放在一边,先把大架构写出来,以免顾首不顾尾。在空间足够的情形下,大架构应该是一个完整的模组,且在整体的观念下,统一处理。

  这种做法,对程式侦错及修改有很大的帮助。因为修改和调整最多、对功能影响最大的,必然是主程式。若主程式都在同一模组中,比较容易得到理想的效果。

  2,副程式:副程式都是一些细节的处理,可以用‘CALL’的方式执行。原则上说来,细节的处理经常重覆发生在不同  的情况下,作为副程式相当有利。只是应该注意调用的手  续,为了效率,通常将需要处理的参数或资料,经由暂存器或者必要时用缓冲器载入。

  既然是数个程式均可共用的副程式,而且此类程式为一独立的过程,所以应该事先分别测试,保证无误。

  此外,各副程式的入口处,宜明白的交待暂存器的使用方式,且要能一目瞭然。

  3,子程式:子程式与副程式有一点不同,就是具备完整的机能。所谓完整的机能,指该段程式可以独立执行、有固定的功能。在应用时,两者没有分别,然而在写作时,子程式的考虑要慎重些。

  4,资料档:资料档也可以视为一种静态的程式,虽然不是执行用的,但却是执行时不可或缺的素材。资料档的设计应该注意空间的利用,等长度的资料结构最具效率,最好保证资料起点为双数,以节省16位元汇流排的执行速度。

  在应用缓冲器时,切忌随意设置,往往程式师们设了一缓冲器,等后来发现没有必要,再想删掉就麻烦大了。所以事先应安排妥当,以便于随时查找和调整。安排的方式视使用的情形而定,有的以模组归类;也可以用字母排序为依据;再不然就加上详细注释说明功能及使用的程式标号。

  5,应用表:在本书第四章第六节将介绍应用表的功能和应用方式,此类表一次设计完成以后,很少需要再修改,为了工作效率,独立成为一个档案,自有其必要性。

  此外,各种程式的命名最好能有代表性,以便于应用;程式不能太大,否则编辑耗时费事;分档时,则要注意标号宣告及各段安排的问题。在磁盘中,应该专辟一个子目录,不要把各种不相干的程式,都混合在一起。

  第二章三、四节中已规定了格式的标准,此处仅再补充一点。即各缓冲器的定义与使用时的长度应相等,否则在编译或联结时,容易发生错误。联结时,有时并无足够的错误讯息,供程式师得知错误产生的原委。最难理解的错误,往往与缓冲器的定义有关,即定义的类型与使用的类型不一致。另外一个情况是段值的改变,其补救方法为在应用时,临时加一「前置定义」。

  所谓「前置定义」是指当暂存器为一字元时,其前应加写BYTEPTR,否则用WORDPTR以确定其值,即可保证安全。

  如:MOV BYTEPTRBBSDOT1,AL

  此外,每当段值有所改变时,都加写一条:

  ASSUME CS:XXX,DS:YYY,ES:ZZZ 

  这种用法,完全是给汇编程式「看」的,程式本身并没有增加任何指令。

  其他规定,请参阅各相关手册。

  二、对资料的认识

  在画布上,所有色彩都是由红、蓝、黄三原色及白色调制而成,瞭解色彩的变化是画家的基本素养。在电脑中,所有的资料则都由二进位数据组成,要写程式,必须对二进位的特性先有深刻的认识。

  绝大部份的程式师,都不知道二进位数据的妙用,充其量能够很快地换算二进位与十进位的数值。再不然,由二进位值领会到图形的点阵排列,如此而已。

  二进位就是开关的观念,把一连串的开关联在一起,其所能发生的作用,完全在于每一个开关、以及各开关组合应用的功能。

  说得明确一点,先要将各种需要设计的功能分析清楚,找出其共通的因素,如果这些因素能用「开」及「关」两个简单的状态代表,则可以用二进位制加以控制。在理论上,一开一关只有两种作用,而两组开关就有222种作用,最理想的设计 是将开关的排列组合数用到极限。

  举例而言,电脑上应用的彩色,就是最理想的设计之一。在电脑中,最基本的应用单位为「字元」(Byte),每一字元有8个「位元」(Bit),相当于8个「开关」。为了要最精简地应用多种彩色,只以三原色与辉度组合,八个开关就能产生256种不同的彩色。兹将各开关所代表的彩色分列如下:

  开关一(bit1):正蓝色

  开关二(bit2):正绿色

  开关三(bit3):正红色

  开关四(bit4):灰色(高辉度)

  开关五(bit5):黑色(低辉度)

  开关六(bit6):浅蓝色

  开关七(bit7):浅红色

  开关八(bit8):浅青色

  ★上述(bitn)是从n=1开始计算。

  应该注意的一点,是电脑的基本单位在于八个开关,不用足就是浪费。如果8个不够,再增加便有16个。所以,因事制宜,在设计的时候,唯有用8的倍数才划算。

  但是,宇宙中的事物,不见得刚好是八的倍数。如果设计的人没有这种认识,不能把所处理的资料,以8为限制条件去划分,就无法利用这种有利的条件,当然,也就得不到最理想的结果。

  所以,要想程式具有最高的效率,首先要把资料整理成为八的倍数值结构。把资料整理为最有效的结构方式,称为「资料结构」,关于这一点,在后面将有较详细的例证。

  每个字元有256种排列组合,即相当于256个十进位的数字。为了方便人的理解,通常将字元写成十六进位形式,并在其数字后加一‘H’,以别于十进位数字。

  兹将十进制与十六进制对应表列于下面:

  二进位值  八进位值  十进位值  十六进位值
    0     0     0     0H
    1     1     1     1H
   *10     2     2     2H
   11     3     3     3H
  *100     4     4     4H
   101     5     5     5H
   110     6     6     6H
   111     7     7     7H
  *1000    *10     8     8H
  1001     11     9     9H
  1010     12    *10     0AH
  1011     13     11     0BH
  1100     14     12     0CH
  1101     15     13     0DH
  1110     16     14     0EH
  1111     17     15     0FH
 *10000    *20     16    *10H

  ★ 凡前有*者表示进位。

  ★★二进位数后应加‘B’,八进位后应加‘O’。

  由上可知,十六进制仍沿用十进位数字,只是到了10时,已无现成数字可用,只好借用英文字母。在程式中,汇编程式为了分辨ASCII字符与十六进制数值,通常规定凡十六进位数值以英文字母开始者,在其字母前加一‘0’。

  三、对暂存器的认识

  暂存器(Register)相当于调色皿,资料相当于色料。把色料放进调色皿里,为的是要得到预定的效果,暂存器对于资料亦然。

  调色皿有大有小,深度有深有浅,其目的是针对不同的情况,以作有效的处理。暂存器也是一样,应用得好,程式会很精简,容易修改、阅读。否则,想到哪一个就用哪一个,没有原则,没有章法,这种程式委实不敢恭维。

  暂存器的重要性,在于处理方便灵活、速度快,占用空间小。不幸8088CPU的暂存器很少,用起来总是捉襟见肘,辛苦异常。正因为此,暂存器的善用与否,成为程式效能高低的关键技术。

  有些程式师不愿意精打细算,经常设定一些「缓冲器」,利用缓冲器可以任意定名、便于记忆的优点,竟把珍贵的暂存器,当作各缓冲器间、搬运资料的交通工具,只见资料不停的搬进搬出。虽然程式师省了点事,但运行速度白白浪费了,空间也被糟蹋了。写这样的组合程式,远不如去用高阶语言。

  当然,缓冲器是有必要的,但也只限于「必要」的情况,而且,在程式规划时,就要考虑各种应用的条件,把缓冲器内的值取出后,一次处理完毕。如果不能一次解决或是经常要用到的资料,则设法放在暂存器中。

  实际上,任何程式不可能在一个过程中,同时需要很多特殊的资料。好的程式师能把复杂的工作处理得有条不紊,功力不够的,往往把简单的事情弄得令人难以理解。8088的暂存器的确是不够用,但是却不至于少到要以缓冲器取代的地步。

  工作的好坏、成败,与人的组织能力有绝对的关系,限于篇幅,我们不能多谈。可是,利用暂存器的特性,来处理繁杂的资料,倒也是训练组织力的方法之一。

  首先,我们应该把暂存器视为工具,瞭解工具的功能、性质,然后要能铭记于心,纯熟地加以运用。

  根据个人的理解,暂存器概分六类:

  1,分段用

  程式段CODESEGMENT        :CS

  资料段DATASEGMENT        :DS

  堆栈段STACKSEGMENT       :SS

  特设段EXTRASEGMENT       :ES

  2,堆栈用:

  堆栈值STACKPOINTER       :SP

  栈用器BASEPOINTER        :BP

  3,记忆转换用:

  源存器SOURCEINDEX        :SI

  终存器DESTINATIONINDEX     :DI

  4,一般用:

  累积器ACCUMULATOR        :AX

  兼用器BASE            :BX

  计数器COUNTER          :CX

  资料器DATA            :DX

  5,标志用:旗号值STATUS       :FLAG

  6,指示用:执行值INSTRUCTIONPOINTER:IP

  为了便于记忆,我们给暂存器定中文名,其定义为:

  凡分段用者率称「段」,做为各段起始位置指示用,其计值方式为:系统中的绝对地址=(本值×16)+各段定址值

  如:资料段为1600H,乘16即为16000H。

  如源存器为1234H,则此源存器在系统中由0算起的地址为:17234H。

  应注意者,各种以「器」定名的暂存器,皆有限用的段,切勿混用。

  凡定名为「值」者,皆为不能用来供程式写作的暂存器。如堆栈值(SP)系指示堆栈所在位置;旗号值(FLAG)表示旗号标志的情况;执行值(IP)则代表程式当前所执行的地址。这些暂存器值并非不能改变,但对技巧尚不够纯熟者,最好保持原值,不要妄动。

  经常使用的「器」有两种,一以16位元为单位,如栈用器、源存器及终存器;另一种则具有两个分别称「高位」及「低位」、各有8位元,可单独使用,也可合并为16位元的暂存器AX,BX,CX,DX。

  暂存器通常作为容器用,但有些多用为记忆区之定址,以便将其中贮存的资料取出应用。前者称为容器功能,可以作计算、逻辑处理等。后者称为定址功能,系供处理各「器」所定位址的资料用。由于8088CPU的定址方式,受限于当初不成熟的设计理念,偏偏IBM独具慧眼,选中了它,所谓城门失火,殃及池鱼,读者不得不多花点功夫,小心应付。

  栈用器(BP)属于堆栈段的记忆位置,系提供给高阶语言结构使用,对组合语言来说,功能不大,但若善于运用,也不无价值。

  源存器(SI)固定指向资料段,将源存器中的资料取出,所指的是取出资料段中的资料。设若

  DS=2000H SI=1234H,则

  SI中的1234H系指系统中2000H×16加上位址值1234H。

  不过,使用者不必去计算,只要知道是由资料段起,位址为1234H即可。

  终存器(DI)较为复杂,通常它是指向资料段,可是有几个指令涉及大量资料转移,需要由源存器搬到终存器。由于受限于分段的设计,为了便于段间应用,所以特别规定:在这种情况下终存器系指向特设段(ES)。也就是说,只能由资料段移向特设段。程式师可以先设定各段的段暂存器,再作转移。若要在同一段中作资料转移,则应使资料段=特设段。

  一般用的暂存器,都可以分成两个8位元、各命名为高、低位暂存器,如:

  累积器:AX 高位AH,低位AL

  兼用器:BX 高位BH,低位BL

  计数器:CX 高位CH,低位CL

  资料器:DX 高位DH,低位DL

  其中累积器的功能最强,可以做乘、除计算,AH尚有贮存旗号的特殊指令。尤其是从记忆区中取值或将值放进记忆区内时,效率最高,如LODS,STOSW等。

 

  由于其功能高,运用灵活,所以宜于打杂,千万不要赋与固定的使命。

  兼用器则有一种重要的特性,它是一般用暂存器中,唯一能自记忆区中读取资料者(XLAT指令除外),所以作为「资料及定址转换」(后文将专门介绍此一功能)方便异常。

  计数器常用作「回路」或次数的记录,也有专用的指令,除非不得已,或者计数用得不多,最好保留备用。

  资料器功能最少,最好固定其用途,选择经常需要应用的资料,置放其中,以便发挥时间空间的最高效率。

图片内容