KEILC51代码优化详细分析
阅读了《与系统应⽤》2005年第10期杂志《经验交流》栏⽬的⼀篇⽂章《Keil 对同⼀端⼝的连续读取⽅法》(原⽂)后,笔者认为该⽂并未就此问题进⾏深⼊准确的分析⽂章中提到的两种解决⽅法并不直接和简单。笔者认为这并⾮是Keil C51中不能处理对⼀个端⼝进⾏连续读写的问题,⽽是对Kei1 C51的使⽤不够熟悉和设计不够细致的问题,因此特撰写本⽂。
本⽂中对原⽂提到的问题,提出了三种不同于原⽂的解决⽅法。每种⽅法都⽐原⽂中提到的⽅法更直接和简单,设计也更规范。(⽆意批评,请原⽂作者见谅)
商空间1 问题回顾和分析
原⽂中提到:在实际⼯作中遇到对同⼀端⼝反复连续读取,Keil C51编译并未达到预期的结果。原⽂作者对C编译出来的汇编程序进⾏分析发现,对同⼀端⼝的第⼆次读取语句并未被编译。但可惜原⽂作者并未分析没有被编译的原因,⽽是匆忙地采⽤⼀些不太规范的⽅法试验出了两种解决办法。
对此问题,翻阅Keil C51的⼿册很容易发现:KeilC51的编译器有⼀个优化设置,不同的优化设置,会产⽣不同的编译结果。⼀般情况缺省编译优化设置被设定为8级优化,实际最⾼可设定为9级优化:
1. Dead code elimination。
团购王2.Data overlaying。
3.Peephole optimization。
4.Register variables。
5.Common subexpression elimination。
6.Loop rotation。
7.Extended Index Access Optimizing。
8.Reu Common Entry Code。
9.Common Block Subroutines。
⽽以上的问题,正是由于Keil C51编译优化产⽣的。因为在原⽂程序中将外设地址直接按如下定义:
unsigned char xdata MAX197 _at_ 0x8000
采⽤_at_将变量MAX197定义到外部扩展RAM 指定地址0x8000。因此,Keil C51优化编译理所当然认
为重复读第⼆次是没有⽤的,直接⽤第⼀次读取的结果就可以了,因此编译器跳过了第⼆条读取语句。⾄此,问题就⼀⽬了然了。
2 解决⽅法
由以上分析很容易就能提出很好的解决办法。
2.1 最简单最直接的办法
火灾扑救
程序⼀点都不⽤修改,将Keil C51的编译优化选择设置为0(不优化)就可以了。选择project窗⼝的Target,然后打开“Options for Target”设置对话框,选择“C51”选项卡,将“Code Optimiztaion”中的“Level”选择为“0:Costant folding”。再次编译后,⼤家会发现编译结果为:
CLR MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV down8,R7
SETB MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
乘法运算MOV R7,A
MOV up4,R7
两次读取操作都被编译出来了。
2.2 最好的⽅法
告诉Keil C51,这个地址不是⼀般的扩展RAM,⽽是连接的设备,具有“挥发”特性,每次读取都是有意义的。可以修改变量定义,增加“volatile”关键字说明其特征:
unsigned char volatile xdata MAX197 _at_ 0x8000;
也可以在程序中包含系统头⽂件;“#include”,然后在程序中修改变量,定义为直接地址:
#define MAX197 XBYTE[0x8000]
这样,Keil C51的设置仍然可以保留⾼级优化,且编译结果中,同样两次读取并不会被优化跳过。
2 3 硬件解决⽅法
原⽂中将MAX197的数据直接连接到数据总线,⽽对地址总线并未使⽤,采⽤⼀根端⼝线选择操作⾼低字节。很简单的修改⽅法就是使⽤⼀根地址线选择操作⾼低字节即可。⽐如:将P2.0(A8)连接到原来P1.0连接的HBEN脚(MAX197的5脚).在程序中分别定义⾼低字节的操作地址:
unsigned char volatile xdata MAX197_L _at_ 0x8000;
unsigned char volatile xdata MAX197_H _at_ 0x8100;
将原来的程序:
MAXHBEN =0;小学六年级语文上册
down8=MAX197;//读取低8位
MAXHBEN =1;
up4=MAX197;//读取⾼4位
改为以下两句即可
down8= MAX197_L;//读取低8位
up4=MAX197_H;//读取⾼4位
3 ⼩结
Keil C51经过长期考验和改进以及⼤量开发⼈员的实际使⽤,已经克服了绝⼤多数的问题,并且其编译效率也⾮常⾼。对于⼀般的使⽤.很难再发现什么问题。笔者曾经粗略研究过⼀下Keil C51优化编洋的结果.⾮常佩服Keil C51设计者的智慧,⼀些C程序编译产⽣的汇编代码.甚⾄⽐⼀般程序员直接⽤汇编编写的代码还要优秀和简练通过研读Kell C51编译产⽣的汇编代码.对提⾼汇编语⾔编写程序的⽔平都是很有帮助的。
由本⽂中的问题可以看出:在设计中遇到问题时.⼀定不要被表⾯现象蒙蔽,不要急于解决,应该认真分析,找出问题的原因.这样才能从根本上彻底解决问题。
附表:Keil C51中的优化级别及优化作⽤
级
别
说明
0常数合并:编译器预先计算结果,尽可能⽤常数代替表达式。包括运⾏地址计算。优化简单访问:编译器优化访问8051系统的内部数据和位地址。
跳转优化:编译器总是扩展跳转到最终⽬标,多级跳转指令被删除。
1死代码删除:没⽤的代码段被删除。
拒绝跳转:严密的检查条件跳转,以确定是否可以倒置测试逻辑来改进或删除。
2数据覆盖:适合静态覆盖的数据和位段被确定,并内部标识。BL51连接/定位器可以通过全局数据流分析,选择可被覆盖的段。酸菜怎么腌制>陈坤的老婆
3窥孔优化:清除多余的MOV指令。这包括不必要的从存储区加载和常数加载操作。当存储空间或执⾏时间可节省时,⽤简单操作代替复杂操作。
4寄存器变量:如有可能,⾃动变量和函数参数分配到寄存器上。为这些变量保留的存储区就省略了。
挑剔怎么读优化扩展访问:IDATA、XDATA、PDATA和CODE的变量直接包含在操作中。在多数时间没必要使⽤中间寄存器。
局部公共⼦表达式删除:如果⽤⼀个表达式重复进⾏相同的计算,则保存第⼀次计算结果,后⾯有可能就⽤这结果。多余的计算就被删除。
Ca/Switch优化:包含SWITCH和CASE的为跳转表或跳转队列。
5全局公共⼦表达式删除:⼀个函数内相同的⼦表达式有可能就只计算⼀次。中间结果保存在寄存器中,在⼀个新的计算中使⽤。
简单循环优化:⽤⼀个常数填充存储区的循环程序被修改和优化。
6循环优化:如果结果程序代码更快和有效则程序对循环进⾏优化。
7扩展索引访问优化:适当时对寄存器变量⽤DPTR。对指针和数组访问进⾏执⾏速度和代码⼤⼩优化。
8公共尾部合并:当⼀个函数有多个调⽤,⼀些设置代码可以复⽤,因此减少程序⼤⼩。
9公共块⼦程序:检测循环指令序列,并转换成⼦程序。Cx51甚⾄重排代码以得到更⼤的循环序列。