tag 标签: 汇编语言

相关帖子
相关博文
  • 热度 32
    2015-9-6 18:12
    2000 次阅读|
    0 个评论
    Keil 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.Reuse 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; 也可以在程序中包含系统头文件;“#includeabsacc.h”,然后在程序中修改变量,定义为直接地址: #define MAX197 XBYTE 这样,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编译产生的汇编代码.对提高汇编语言编写程序的水平都是很有帮助的。 由本文中的问题可以看出:在设计中遇到问题时.一定不要被表面现象蒙蔽,不要急于解决,应该认真分析,找出问题的原因.这样才能从根本上彻底解决问题。 转自畅学电子网。
  • 热度 20
    2015-9-6 16:38
    1742 次阅读|
    0 个评论
    Keil 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.Reuse 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; 也可以在程序中包含系统头文件;“#includeabsacc.h”,然后在程序中修改变量,定义为直接地址: #define MAX197 XBYTE 这样,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程序编译产生的汇编代码.甚至比一般程序员直接用汇编编写的代码还要优秀和简练通过研读Keil C51编译产生的汇编代码.对提高汇编语言编写程序的水平都是很有帮助的。 由本文中的问题可以看出:在设计中遇到问题时.一定不要被表面现象蒙蔽,不要急于解决,应该认真分析,找出问题的原因.这样才能从根本上彻底解决问题。 转自畅学电子网。
  • 热度 21
    2015-1-22 16:50
    1431 次阅读|
    0 个评论
    带有C/C++表达式的内联汇编格式为:   __asm__ __volatile__("Instruction List" :Output: Input : Clobber/Modify);   其中每项的概念及功能用法描述如下:   1、__asm__   __asm__是GCC 关键字asm 的宏定义:   #define __asm__ asm   __asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。        2、Instruction List    Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或__asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List 为空的内联汇编表达式都是没有意义的。     比如:__asm__ ("":::"memory");   就非 常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。 当在"Instruction List"中有多条指令的时候,可以在一对引号中列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符 (\n)将它们分开. 综上述:(1)每条指令都必须被双引号括起来(2)两条指令必须用换行或分号分开。   例如: 在ARM系统结构上关闭中断的操作   int disable_interrupts (void)   {   unsigned long old,temp;   __asm__ __volatile__("mrs %0, cpsr\n"   "orr %1, %0, #0x80\n"   "msr cpsr_c, %1"   : "=r" (old), "=r" (temp)   :   : "memory");   return (old 0x80) == 0;   }   3. __volatile__   __volatile__是GCC 关键字volatile 的宏定义   #define __volatile__ volatile   __volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。   4、Output   Output 用来指定当前内联汇编语句的输出   例如:从arm协处理器p15中读出C1值   static unsigned long read_p15_c1 (void)   {   unsigned long value;   __asm__ __volatile__(   "mrc p15, 0, %0, c1, c0, 0 @ readcontrolreg\n"   : "=r" (value) @编译器选择一个R*寄存器   :   : "memory");   #ifdef MMU_DEBUG   printf ("p15/c1 is = %08lx\n", value);   #endif   return value;   }   5、Input   Input 域的内容用来指定当前内联汇编语句的输入Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)   例如:向arm协处理器p15中写入C1值   static void write_p15_c1 (unsigned long value)   {   #ifdef MMU_DEBUG   printf ("write %08lx to p15/c1\n", value);   #endif   __asm__ __volatile__(   "mcr p15, 0, %0, c1, c0, 0 @ write itback\n"   :   : "r" (value) @编译器选择一个R*寄存器   : "memory");   read_p15_c1 ();   }   6.、Clobber/Modify    有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这一点考虑进去。那么你就可以在 Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。   例如:   __asm__ ("mov R0, #0x34" : : : "R0");   寄存器R0出现在"Instruction List中",并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定"R0",以让GCC知道这一点。    因为你在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作表达式使用"r"约束,让GCC为你选择一个寄 存器时,GCC对这些寄存器是非常清楚的——它知道这些寄存器是被修改的,你根本不需要在Clobber/Modify域再声明它们。但除此之外,GCC 对剩下的寄存器中哪些会被当前的内联汇编修改一无所知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在Clobber/Modify 中声 明它们,让GCC针对这些寄存器做相应的处理。否则有可能会造成寄存器的不一致,从而造成程序执行错误。   如果一个内联汇编语句的 Clobber/Modify域存在"memory",那么GCC会保证在此内联汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后, 如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。因为这个时候寄存器中的拷贝已经很可能和内存处的内容 不一致了。   这只是使用"memory"时,GCC会保证做到的一点,但这并不是全部。因为使用"memory"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。   例如:   int main(int __argc, char* __argv[])   {   int* __p = (int*)__argc;   (*__p) = 9999;   __asm__("":::"memory");   if((*__p) == 9999)   return 5;   return (*__p);   }    本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。   另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中   # define barrier() _asm__volatile_("": : :"memory")   主要是保证程序的执行遵循顺序一致性。有的时候你写代码的顺序,不一定是最终执行的顺序,这个是处理器有关的。
  • 热度 31
    2014-12-18 11:13
    2588 次阅读|
    2 个评论
    更新时间:2014年9月17日(没有比我还全,整理还认真的了)python更新到47讲,windows sdk  更新到14课时,   持续更新中。。。 内容大小:30G左右 本视频目录如下: 第一部:汇编语言      77课时     完结 第二部:win32汇编     56课时     完结 第三部:解密系列   与官网一样    完结 第四部:数据结构与算法 98课时    完结 第五部:C语言         65课时     完结 第六部:C++           48课时     完结 第七部:delphi        55课时     完结 第八部:Python        47课时   持续更新中。。。 第九部:windows sdk    14课时    持续更新中。。。 下载地址: http://item.taobao.com/item.htm?spm=a1z10.5.w4002-7158798009.20.gxzCxvid=42110740481
相关资源