| 查看: 729 | 回复: 10 | |||
| 当前主题已经存档。 | |||
| 当前只显示满足指定条件的回帖,点击这里查看本话题的所有回帖 | |||
sdlj8051金虫 (著名写手)
|
[交流]
[转贴]汇编艺术
|
||
|
汇编语言可以说是未经整理的、原始的计算机语言,读者们大可下一番功夫,找出 其应用的规则,以发挥最高的效率。在下面,我仅就个人的经验,提供一些浅见,以供 切磋研讨。 要写好程序,首先应熟记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,除非不得已,不可随便使用。有时由于子程序说明 不清楚,程序员为了安全,又懒得检查,便把寄存器统统堆在堆栈上。尤其是在系统程 序或子程序中,经常有到堆栈上堆、取的动作。实际上,花点功夫,把寄存器应用查清 楚,就可以增进不少效率。 要知道,系统程序及某些子程序常常应用,有关速度的效率甚大,如果掉以轻 心,就是不负责任! 保存原值的方法很多,其中较有效率的是放到一些不用的寄存器里。以我的经 验,堆栈器用途最少,正好用作临时仓库。但最好的办法,还是把程序中寄存器的应用 安排得合情合理,不要浪费,以免堆得太多。 还有一种方法,是在该子程序中,不用堆栈的手续,但另设一个入口,先将寄 存器堆起,再来调用不用堆栈的子程序。这两个不同的入口,可以分别提供给希望快速 处理,或需要保留寄存器原值者调用。 当然,更简单有效的方法,则是说明本段程序中某些寄存器将被破坏,而由调 用者自行保存之。 二、程序要条理通顺 1,在比较判断的过程中,邻近值不必连比。 CMP AL,0 JE ABCD0 CMP AL,1 JE ABCD1 CMP AL,2 JE ABCD2 .. 应为: CMP AL,1 JNE ABCD0 ABCD1: .. 在标题为ABCD0 中,再作: JA ABCD2 这种做法端视时间效益而定,似此 ABCD1之速度最快。 2,未经慎思的流程: ADD AX,4 ABCD: STOSW ADD AX,4 ADD DI,2 LOOP ABCD .. 稍稍动点脑筋,就好得多了: ABCD: ADD AX,4 STOSW INC DI INC DI LOOP ABCD .. 3,错误的处理方式: MOV BX,SI ABCD: MOV BX,[BX] OR BX,BX JZ ABCD1 MOV SI,BX JMP ABCD ABCD1: LODSW .. 上例应该写成: MOV BX,SI ABCD: LODSW OR AX,AX JZ ABCD1 MOV SI,BX JMP ABCD ABCD1: .. 4,错误的流程: TEST AL,20H JNZ ABCD CALL CDEF[BX] JMP SHORT ABCD1 ABCD: CALL CDEF[BX+2] ABCD1: .. 应该写成: TEST AL,20H JZ ABCD INC BX INC BX ABCD: CALL CDEF[BX] ABCD1: .. 5,下面是时间的损失: PUSH DI MOV CX,BX REP STOSB POP DI PUSH,POP 很费时间,应为: MOV CX,BX REP STOSB SUB DI,BX 同理,很多时候稍稍想一下,就可省下一些指令: PUSH CX REP MOVSB POP CX SUB DX,CX 为什么不干脆些? SUB DX,CX REP MOVSB 6,有段程序,很有规律,但却极无效率: X1: TEST AH,1 JZ X2 MOV BUF1,BL X2: TEST AH,2 JZ X3 MOV BUF2,DX ; 凡双数用DX,单数用BL X3: TEST AH,4 JZ X4 MOV BUF3,BL X4: .. ; 以下各段与上述程序相似 X8: .. 这种金玉其表的程序,最没有实用价值,改的方法应由缓冲器着手,先安排成 序列,由小而大如: BUF1 DB ? BUF2 DW ? BUF3 DB ? BUF4 DW ? .. 然后,程序改为: MOV DI,OFFSET BUF1 ; 第一个缓冲器 MOV AL,BL MOV CX,4 X1: SHR AH,1 JZ X2 STOSB X2: SHR AH,1 JZ X3 MOV [DI],DX INC DI INC DI X3: LOOP X1 7,回路最怕千回百转,不畅不顺,如: SUB AH,AH ABCD: CMP AL,BL JB ABCD1 SUB AL,BL INC AH JMP ABCD ABCD1: .. 以上 ABCD1这个入口是多余的,下面就好得多: MOV AH,-1 ABCD: INC AH SUB AL,BL JA ABCD ADD AL,BL ; 还原 .. [ Last edited by sdlj8051 on 2007-2-10 at 10:01 ] |
» 猜你喜欢
寻求一种能扛住强氧化性腐蚀性的容器密封件
已经有7人回复
到新单位后,换了新的研究方向,没有团队,持续积累2区以上论文,能申请到面上吗
已经有8人回复
申请2026年博士
已经有6人回复
请问哪里可以有青B申请的本子可以借鉴一下。
已经有5人回复
天津工业大学郑柳春团队欢迎化学化工、高分子化学或有机合成方向的博士生和硕士生加入
已经有5人回复
2025冷门绝学什么时候出结果
已经有7人回复
请问有评职称,把科研教学业绩算分排序的高校吗
已经有6人回复
Bioresource Technology期刊,第一次返修的时候被退回好几次了
已经有7人回复
请问下大家为什么这个铃木偶联几乎不反应呢
已经有5人回复
康复大学泰山学者周祺惠团队招收博士研究生
已经有6人回复
sirenshen
木虫 (正式写手)
- 应助: 1 (幼儿园)
- 金币: 4952.1
- 帖子: 691
- 在线: 165.3小时
- 虫号: 86685
- 注册: 2005-08-16
- 性别: GG
- 专业: 人工智能与知识工程
5楼2006-11-17 15:06:47













回复此楼