灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3723回复:0

[系统教程]汇编语言---套装软件制作(2)

楼主#
更多 发布于:2011-10-11 19:57
第三节  程式合并

    我所见过的各种组合程式虽不算多,但至少有百余个了。毛病最多的当然是缺乏完整的规划,其次则是信马游缰,一份不折不扣的流水帐!明明大门口在东边,程式硬要朝西,直到游完了大观园,天黑了,才出东门!
    这种程式我收集了一大叠,可是举来做例子,却心有余而力不足。原因无他,实在不耐烦照抄一遍,一见到就头痛!
    电脑最强的功能,便是处理繁杂重复的工作,为什么一般程式师居然存心与电脑争风吃醋呢?不说别的,光把程式输入到电脑中,就要花上几个月宝贵的光阴,真值得这样做吗?
    有一份程式,足足有四十多页,我只略作调整,便缩小到十页,处理速度则快了五倍。为什么会差这样远呢?很简单,有些人不喜欢用大脑,久而久之,习惯成自然,大脑就生了铁銹!除了等因奉此,什么都不会想了。
    要想做一个优秀的程式师,第一个条件是不能偷懒,第二个条件则要有分析观察的习惯,第三个也是最重要的,则是要有追求完美的精神。程式师要像艺术家,不论是自己的或是别人的程式,都要一而再、再而三地玩味改良。
    我曾见过一个扫地的妇人,她不管在哪里,见不得有任何脏乱。这种人才值得尊敬,这种精神是伟大的,与她的职业丝毫无关!
    程式写得不够精简,有三个原因,第一个是程式师无能,这种程式能够写完,可以运行,已算相当难得了;第二个原因是不懂技巧,硬桥硬马的干, 不知什么是效率,也不知道如何达成。自己写的程式都不见得看得懂,遑论他人的?第三则是根本缺乏敬业精神,敷衍塞责,这种人我最瞧不起。
    写程式之初,如果把任务瞭解清楚,然后分析因素,分割模组。所有类似的情况都合并到一处,再以变数代替,统一执行。这原本是份内的工作,前述的情况根本不可能发生!
    问题是发生了以后怎么办呢?我建议最好重写,如果一定要改,只好采用程式合并的技巧,浓缩一下。
    合并的目的是为了增进效率,而合并的方法则因情况不同而异,就像人生了病,必须先查出病因,否则无法下药。我试着以所知道的一些例证,简要地解说如后。

一、过程的合并:

    要做过程的合并,首先要查明下列各点:
  1,首先找出过程类似的,全部移到一堆,如果找不到,那就没救了。
    然而,这种程式要就是太小,根本不可能有类似的情况,再不就是写作时杂乱无章,信马游缰。分明有类似的过程,但没有共通的原则,无从浓缩。当然,也可能有些程式,因工作量及处理的细节太多,以致无法浓缩。

  2,在类似的程式中,找寻相异的指令或流程,再若没有,那就是重复了,正宜合并。

  3,把相异的指令或流程用变数取代,或将不同程式之入口放在暂存器里。

  4,将各程式在应用该流程前,设好变数及使用的暂存器。

  5,合并相似的程式段,不同处应用变数取代之。

    下面举一实例,系一绘图程式之片断,兹改变原用标题,并将分散在各处若干不同之段,列述如下:
  189: MASK    PROC    NEAR
  190:        MOV    DX,3C4H
  191:        MOV    AL,2
  192:        OUT    DX,AL
  193:        MOV    DX,3C5H
  194:        MOV    AL,PCOLOR
  195:        OUT    DX,AL
  196:        RET
  197: MASK    ENDP
  …
  380:        MOV    DX,03CEH
  381:        MOV    AL,3
  382:        OUT    DX,AL
  383:        MOV    AL,18H
  384:        INC    DX
  385:        OUT    DX,AL
  386:        RET
  …
  490:        MOV    DX,3CEH
  491:        MOV    AL,3
  492:        OUT    DX,AL
  493:        MOV    DX,3CFH
  494:        MOV    AL,0H
  495:        OUT    DX,AL
  496:        RET
  …
  589: CROSS    PROC    NEAR
  590:        MOV    DX,3C4H
  591:        MOV    AL,2
  592:        OUT    DX,AL
  593:        INC    DX
  594:        MOV    AL,0FH
  595:        OUT    DX,AL
  596:        RET
  597: CROSS    ENDP
  …

    这样的段落有十多处,看来每个都略有不同,似乎不能合并。然而仔细分析,显然是程式师训练不够,把一个非常有规则的程式,安排得非常紊乱,以致到这个地步。
  

    我们先归纳问题,决定如何合并。第一,上述各段程式,应该统一作为子程式;第二,全部变数只有四个,其中两个是传送值,两个是输出入埠。后者有连续关系,等于只有一个。因此,在调用此子程式前,应先令DX为输出入埠,再将变数装入AX中,一次调用即可。此子程式如下:
  300: SUB:
  301:        OUT    DX,AL
  302:        INC    DX
  303:        MOV    AL,AH
  304:        OUT    DX,AL
  305:        RET
    这样简短的子程式,有无必要,端视时空的效益而定。不论怎样整理,都远比原来的要好。
    另外有种情况,更为可怕,就是在键盘输入后,用流程方式,一一比较输入码,再一一分别处理。
    比如说,为了检查游标键的左、右、上、下等八个方向的移动,以便作相应的处理,程式居然写成:
  100: PP1:    MOV    AH,0
  101:        INT    16H
  102:        CMP    AX,4800H    ;↑键
  103:        JNE    NEXT1
  104:        CALL    MOVDATA     ;SET BUFFERS
  105:        CALL    SETDLT        ;SET INCREMENT
  106: NXT01:
  107:        CALL    DOTUP
  108:        LOOP    NXT01
  109:        CALL    XORDOT        ;SET NEW DOT
  110:        CALL    XYDISP        ;DISP NEW XXX,YYY
  111:        JMP    PP1
  112: NEXT1:
  113:        CMP    AX,5000H    ;↓键
  114:        JNE    NEXT2
  115:        CALL    MOVDATA     ;SET BUFFERS
  116:        CALL    SETDLT        ;SET INCREMENT
  117: NXT02:
  118:        CALL    DOTDOWN
  119:        LOOP    NXT02
  120:        CALL    XORDOT        ;SET NEW DOT
  121:        CALL    XYDISP        ;DISP NEW XXX,YYY
  122:        JMP    PP1
  123: NEXT2:
  124:        CMP    AX,4B00H    ;←键
  125:        JNE    NEXT3
  …
    这段程式总共要检查八次,才能确定是否有游标移动以及哪个游标在移动。然后,还要一一检查其他变化,共有十八种有效码。我实在佩服这种程式师,不但有无比的耐性,还有非凡的想像力,居然能把一段极为简单平凡的程式,写得这样的精彩动人!
    如果是我,我会写得毫无趣味:
  100: PP1:    SUB    AH,AH
  101:        INT    16H
  102:        OR    AL,AL
  103:        JNZ    PP1        ;AL 非0无效
  104:        MOV    BH,AL
  105:        MOV    BL,AH
  106:        SUB    BL,47H        ;最小之字标键
  107:        JLE    PP1        ;非处理范围
  108:        SHL    BX,1
  109:        CALL    FUNC[BX]
  110:        JMP    PP1
    这是主流程,程式短,速度快,维护容易,一眼看过去,有什么错误立刻分明。
 …
 1000: FUNC    DW    NEXT02        ;↖
 1001:        DW    NEXT0        ;↑
 1002:        DW    NEXT04        ;↗
 1003:        DW    PPRET        ;无效
 1004:        DW    NEXT2        ;←
 1005:        DW    PPRET        ;无效
 1006:        DW    NEXT4        ;→
 1007:        DW    PPRET       ;无效
 1008:        DW    NEXT12        ;↙
 1009:        DW    NEXT1        ;↓
 1010:        DW    NEXT14        ;↘
 …
    因为这是子程式,加一段、减一段容易非常。
    即使是子程式,也有很大的考究,就以前段来说,在 104至110 之间,就值得三思。
  104:        CALL    MOVDATA     ;SET BUFFERS
  105:        CALL    SETDLT        ;SET INCREMENT
  106: NXT01:
  107:        CALL    DOTUP
  108:        LOOP    NXT01
  109:        CALL    XORDOT        ;SET NEW DOT
  110:        CALL    XYDISP        ;DISP NEW XXX,YYY
    首先,104 和105 会重复多次,109 及110 亦然,为什么不合并为一呢?这也是很常见的程式合并手法,两次调用合为一次,速度及空间都较为经济。
    在子程式 SETDLT 之前,先调用一次 MOVDATA,另XYDISP也是一样,首先备妥:
 3000: SETDATA:
 3001:        CALL    MOVDATA     ;假设本程式有他用
 3002: SETDLT:
 3003:          …
 …
 3100: XYDIDOT:
 3101:        CALL    XORDOT        ; 同上
 3102: XYDISP:
 3103:        …
 …
    再来设计NEXT0 的子程式:
  110: NEXT0:
  111:        CALL    SETDATA
  112: NXT01:
  113:        DOTUP  应搬至此,无需设为子程式。
  …
  120:        LOOP    NXT01
  121:        JMP    XYDIDOT     ; 如有必要,可先
                                    ; 设好参数
    这样合并一下,效果决不止高上十倍,等到真正学会了程式的技巧,写作时速度也可以提高数倍。二、分支的处理:

    分支是程式中不可避免的手段,使用得好,整个程式气势一贯,有行云流水之妙。
    前面的例子根本不具分支的条件,故不能算是分支不良,而是程式师观念错误。
    下面再举一例,由于分支不良,以致程式支离破碎。这是一则计算拋物线的快速程式,妙在没有用乘除法,也没有任何函数。其中有几段是这样的:
  100: BEG00:
  101:            CMP    BP,BUFY
  102:?    JLE    BE7
  103:           OR    CX,CX
  104:             JG    BE20
  105:            MOV    AX,BP
  106:?    SHL    AX,1
  107:           DEC    AX
  108:           JL    BE10
  109: BE2:
  110:             CALL    BE1
  111:            JC    BEG00
  112:          CALL    BE3
  113:            JMP    BEG00
  …
  120: BE14:
  121:?    LODSW
  122:           CMP    AH,1FH
  123:             JGE    BE141
  124:            LOOP    BE14
  125:      POP    DI
  126:          POP    CX
  127:            MOV    SI,DI
  128:              JMP    BE142
  129: BE141:
  130:            POP    DI
  131:          POP    CX
  132:          MOV    SI,DI
  …
  150: BE10:
  151:          CALL    BE1
  152:            JMP    BEG00
  153: BE20:
  154:              MOV    AX,CX
  155:?    SUB    AX,DX
  156:           SHL    AX,1
  157:             DEC    AX
  158:            JLE    BE2
  159:?    CALL    BE3
  160:           JMP    BEG00
  161: BE1:
  162:?    INC    DX
  163:           ADD    CX,DX
  164:             ADD    CX,DX
  165:            INC    CX
  166:            ADD    DI,BUFX
  167:           CMP    DI,BX
  168:             JLE    BE1RET
  169:            CALL    BE01
  170:            SUB    DI,BX
  171: BE1RET:
  172:           RET
  …
  190: BE01:
  191:?    MOV    AL,1
  192:           CMP    [SI+1],AL
  193:             JNZ    BE011
  194:            INC    BYTE PTR [SI+1]
  195:              RET
  …
  200: BE141:
  …
    全部程式并不大,不过一百多条指令,但是稍加改进,却可以省却廿多条指令,速度也会加快。重点在于106 到113 的分支错误,以致于多出BE10 BE20 BE3 BE01等段程式出来。
    照理,BE1 BE3 BE01都不该另设子程式,BE14也应改写,如此,整个程式就完全不同了。
    原来由 105为:
  105:            MOV    AX,BP     ;★无必要
  106:              SHL    AX,1     ;★无必要
  107:            DEC    AX     ;★无必要
  108:            JL    BE10
  109: BE2:
  110:             CALL    BE1     ;★合并后,无需调用
  111:            JC    BEG00     ;★另作分支
  112:           CALL    BE3     ;★也无必要调用
  113:             JMP    BEG00
    现改为:
  107: BE1:              ;原为DEC AX分支处理
  108:              INC    DX     ;原161子程式作主流程
  109:            ADD    CX,DX
  110:          ADD    CX,DX
  111:            INC    CX
  112:              ADD    DI,BUFX
  113:          CMP    DI,BX
  114:            JLE    BE11
  115: ;          CALL    BE01     ;本子程式重写如下:
  116:            CMP    BYTE PTR[SI+1],1
  117:             JNE    BE1A
  118:            INC    BYTE PTR[SI+1]
  119: BE1A:
  120:            SUB    DI,BX
  121:            JC    BEG00     ;原111
  122:            …          ;原BE3 程式
  …
    又 125条三个指令也是分支错误,白白浪费。
  120: BE14:
  121:           LODSW
  122:             CMP    AH,1FH
  123:            JGE    BE141
  124:          LOOP    BE14
  125:            POP    DI     ;★可以省略
  126:              POP    CX     ;★可以省略
  127:          MOV    SI,DI     ;★可以省略
  128:            JMP    BE142     ;★可以省略
  129: BE141:
  130:              POP    DI
  131:            POP    CX
  132:          MOV    SI,DI    ;127移到此
  133:            JNZ    BE142     ;128移到此
  134:              …

 第四节  定案包装

一、手册:

    手册写作本来与程式写作无关,但由于一般程式师都不知道手册的重要性,往往程式写得极佳,而市场口碑却不良,以致惨遭滑铁泸之败。
    实际上,当今市场的趋势,都倾向于萤幕提示,以致于手册仅具辅助作用,帮助使用者理解各种功能的发挥而已。
    问题就出在这里,一个功能的介绍、说明,与该功能应用的发挥,完全是不同的层次。「萤幕提示」经常由程式师自行制作,而程式师对文字概念的应用及理解能力,往往并不太高明,其结果可想而知。
    手册应该有专人写作,这种人既要对文字概念应用裕如,又要充份瞭解电脑的功能。难的是,培养一个程式师,了不起三个月到半年,而一个能达意的作家,起码需要三至五年。遗憾的是,一般电脑公司没有这种眼光,以为写程式需要技术,手册则随便找人应付了事。
    手册的重要性,并非仅止于此,一个有价值的程式,一定有周详的计划,有制作的蓝图。这种计划及蓝图,经过文字概念上的整理,应该就是手册本身。换句话说,有良好规划的程式,必然是先有手册作为蓝图,再根据手册制作程式。

二、版本:

    程式完成以后,除非一些特殊的原因,只要有实用价值,必然需要不断改进、强化。
    这一来就面临版本更新的问题,程式师在制作之初,必须事先考虑周全。不要希望一次把程式写得尽善美,完整无缺,不仅那是不可能的梦想,也是自找麻烦。
    任何一个人,即使是不世天才,也不可能经历人间所有的事件。而程式所需要适应的范围,则是动态的、随着人的知识及经验不断增长。因此,一个崭新的程式一旦问世,就成为人世间的新生事物,人的经验扩展后,新的需求即接踵而至。刚刚完成的程式,在完成的那一剎,就已成为过去式。
    所以在程式规划时,必须高瞻远瞩,考虑得越是周全,程式的生命力越是旺盛。同时,在另一方面,程式必须交到使用者手中,才有实际的价值。是以如何在周全的规划,和尽快的完成工作之间,作有效的斟酌取舍,则是个难题。
    解决的方法之一,就是利用「版本」观念,将产品分为数个时期。这样,不仅产品可以很快地交到使用者手中,而且使用者可以提供其应用经验的回馈,更有利的,是程式得以不断地增长、成熟、完善。
    有了版本的观念,还需要对版本的制作有明确的计划,每一个版本的档案维护,修订更正,都要有专人负责。否则,当已经上市的版本还需要修改,而新的版本业已开始设计,若是一个不小心,分不清档案属于哪个版本时,其后果之不堪,将非局外人所能领会的了。

三、包装:

    此处所提的「包装」,不是商业上所谓的如何将产品美化伪装起来。而是指一个程式交到使用者手中时,应该具备哪些必备的,哪些选用的「配备程式」。
    一般大型的应用程式,经常提供很多片磁盘,要先执行一个很复杂的「初始化」程式,才能使用。如果采用组合语言制作,其目的本就是为了节省空间。空间小了,应该可以避免这种多余的手续。
    这就是包装所要考虑的问题,比如说,在我们的“聚珍整合系统”中,附有如下一些配备程式及手册:
  1,功能、操作提示或手册:
    1-1 sm.hlp:在功能提示态下,说明各功能、操作方式及注意事项。
    1-2 smvqoq.exe:聚珍整合系统操作手册阅览程式
        smvqo1.dat--smvqod.dat :操作手册资料档。
  2,smjooh.exe:繁、简体档互转程式
    smjooh.tab:繁、简体转换对照表。
  3,smjopa.exe:本系统与park文书档资料互转用。
  4,smjob5.exe:为转换其他系统生成的文书档资料用。
  5,smjib5.exe:转换dbase iii 资料档。
    这些程式及档案,都要放在同一片磁盘中,不仅为了方便省事,也可降低成本。
    在我们的经验中,这些工作说来容易,做来却大费周章。唯有在事先做好妥善的规划,最后才能省时省事,达到理想的预期效果。
    仅以萤幕提示为例,由于资料所占空间太大,就导致了极大的困难。如果事先有准备,将资料作适当的压缩,显然会省却不少麻烦。
    此外,手册的印刷,磁盘的复制,所有一切应行考虑的,都要事先想清楚。要知道,一个应用软件,其成本完全在开发及最后的包装过程,为了成功,代价是必须先付出的。


 





喜欢0 评分0
游客

返回顶部