打印

难道我发现了Keil的BUG?请各位高手看一看——有图有真相

[复制链接]
楼主: XIANSir
手机看帖
扫描二维码
随时随地手机跟帖
21
XIANSir|  楼主 | 2011-2-28 20:29 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
我好像明白ANL A,#B(0xF0)是什么意思了,好像是先把寄存器B赋值0xF0,然后再让A和B逻辑与,并把结果存放在A中。

不知道对不对啊???
那么这条怎么解释:
ANL A,#ACC(0xE0)
好像用前面的解释说不通啊。

急盼高手告知——在线等待

使用特权

评论回复
22
ayb_ice| | 2011-2-28 20:32 | 只看该作者
ANL A,#B(0xF0)应该是ANL A,#0xF0

使用特权

评论回复
23
XIANSir|  楼主 | 2011-2-28 20:43 | 只看该作者
汇编阅读中,顺便感慨一下:
为了节省内存,对于标志量(只能取0和1的变量)的定义方法有两种:
1、
struct {
                uchr b_1        : 1;
                uchr b_2        : 1;
                uchr b_3        : 1;
                uchr b_4        : 1;
                uchr t_1        : 1;
                uchr t_2        : 1;
                uchr t_3         : 1;
                uchr t_4        : 1;
        }pin = {1,0,0,0};
2、
bit b_1,b_2,b_3,b_4;
bit t_1,t_2,t_3,t_4;

第一种是标准C语言中的位域法,第二种是C51扩展的位变量(标准C中没有),
使用位域的好处是显而易见的,标准C,任何单片机都支持,利于移植。而且如果需要好几组类似的标志的话——比如交通灯的红灯、绿灯、黄灯各需要一组状态变量,那么我们只要定义三个结构体变量就可以了。

但是对于位变量bit法的话,很难移植,而且像上面说的那种情况就得定义三套类似的变量,这样就会造成程序开头一大堆的变量定义——不夸张的说,我正在处理一个有100多全局变量程序,而整个程序只有不到4K大小。

可见位域法有着极大的优点。

但是通过今天读汇编代码才发现:bit变量法也有着相当的优势啊。
对位域变量中的位和bit位变量进行相同的运算(如,逻辑与),bit变量仅需要3条语句,而位域变量则需要十几条指令——运行速度差距可想而知了。另外,bit变量的操作很容易读懂(在会变中),我只用了几分钟就全都读懂了,可是对位域变量的操作则非常的难读,绕来绕去,人都晕了:lol

另外,为什么Keil不直接把位域变量放在51内存的bit区呢?这样岂不是两全其美了吗????真是搞不懂

使用特权

评论回复
24
XIANSir|  楼主 | 2011-2-28 20:49 | 只看该作者
22# ayb_ice
偶,这样啊,谢谢啊

偶:L太菜了,没脸见人了

使用特权

评论回复
25
XIANSir|  楼主 | 2011-2-28 20:52 | 只看该作者
20# chen3bing
谢谢,希望大侠不要光顾着为了自己对了而欣赏:lol,也和俺一道试试想为什么上面的不对,尤其是图片1和图片3中的为什么一个对一个错


谢谢

使用特权

评论回复
26
XIANSir|  楼主 | 2011-2-28 20:55 | 只看该作者
19# ayb_ice
谢谢!但是看到你的提示俺一下子没反应过来,直到看到22楼的解释才明白了您的意思——唉,真是太惭愧了:L
22楼:
ANL A,#B(0xF0)应该是ANL A,#0xF0

使用特权

评论回复
27
dtmcp| | 2011-2-28 21:14 | 只看该作者
位逻辑要用逻辑运算符

使用特权

评论回复
28
XIANSir|  楼主 | 2011-2-28 21:18 | 只看该作者
本帖最后由 XIANSir 于 2011-2-28 21:21 编辑

19# ayb_ice
首先,对于bit定义的位变量,使用按位取反“~”符号肯定没错,因为下面的四张程序代码就是对bit变量进行“~”操作,而结果都是对的。而且经过跟踪汇编代码,我发现对位变量的按位取反操作其实是使用取补指令“CPL  C”完成的——这很自然,我认为任何C51的编译器都会把对位变量的按位取反“~”操作编译成取补指令“CPL”的。而对于CPL的作用,8051指令集上有这样的功能描述:Description: CPL complements operand, leaving the result in operand. If operand is a single bit then the state of the bit will be reversed.
可见,对于位变量(bit变量)进行“~”操作肯定是没有任何问题的。


其次,对于位域定义位成员变量,虽然在C中它被看作是位变量,但对于位域的操作都是通过对整数的逻辑操作实现的,所以对于位域成员使用按位取反操作“~”,根本就不是对位变量的按位取反“~”操作,而是对整数的按位取反“~”操作,所以根本也就不存在你所说的对于位变量不应该使用“~”而应该使用“!”这个问题。


综上所述,你的建议不成立。

使用特权

评论回复
29
XIANSir|  楼主 | 2011-2-28 21:31 | 只看该作者
奇怪:
ANL  A,#ACC(0xE0)
上面的指令是把A和ACC进行相与吗??
A和ACC不是同一个寄存器吗??
那自己和自己相与岂不是结果还是原值啊??

但是,在这条指令执行前,A和地址0xE0处的值都是0x21,但是上面那一条指令执行后,A和地址0xE0处的值都变成0x20了,这是怎么回事啊??

使用特权

评论回复
30
XIANSir|  楼主 | 2011-2-28 21:50 | 只看该作者
编译器也太傻了吧,看下面的图:

把0x00赋给R7,然后把R7赋值给A,那A不明摆着就是0x00吗。
下面竟然还对A进行逻辑与操作和左移操作。对0x00进行这些操作有意义吗???是只有KEIL编译器这么笨还是所有的编译器都这么笨啊??要是编译器都这水平的话,那俺就放心了——那些鼓吹人工智能将统治人类的预言家也可以休息休息睡了

使用特权

评论回复
31
XIANSir|  楼主 | 2011-2-28 22:50 | 只看该作者
经过对代码的跟踪,我现在确信是编译器的BUG了,我将简要介绍BUG产生的造成原理,如有不妥之处,敬请高手指点:请看下面的一段汇编代码:


第一句是C语言,下面是它对应的汇编代码。
图上的两个红色方块依次分别对~pin.b_1和~pin.b_2是否为0进行判断,由于两块代码功能相同,我就把第一个方块里面的三条语句进行一下讲解:
ANL A,0x01
A中存储的是结构体pin,所以这一句的作用就是清0其他位而只保留pin的第一位——也就是pin.b_1。这样我们就把pin.b_1的值取出来了——存于A中
CPL  A
这一句对A进行按位取反,请注意,A是怎么来的,从上面那一句我们可以轻易的判断:A的高七位肯定是0,A的最低一位不确定,那么按位取反后,A的值就应该是而且只能是0xFF或0xFE中的一个。也就是说,不管pin.b_1是多少,这一步执行后A的值只可能是0xFF或0xFE之一。
JZ C:0193
这句指令的意思是如果A的值为0,那么就跳转到0x0193处,否则就接着下面的指令执行。那么我们上面刚刚说了什么呢:“不管pin.b_1是多少,这一步执行后A的值只可能是0xFF或0xFE之一。”,那么这里就会出现这种情况:不管pin.b_1是多少,本句指令肯定不会执行跳转,而是一定执行下面紧接着的指令。

好,在方块1和方块2之间没有跳转指令,那么程序就一定会执行到方块2那里去,方块2的代码是判断pin.b_2是否为0,而且判断的方法和上面的方块1完全相同,那么我们就得到了下面的结论:不论pin.b_2是否为0,方块2中的条件跳转指令都不会执行跳转,而是一往无前的执行完方块中的每一条指令,然后继续执行方块2下面紧接着的那条指令。

综上两个方块的分析可知:不论pin.b_1和pin.b_2的值是多少,程序都会从地址0x0180处一直逐条执行到0x018F,在0x018F处的三条指令其实很容易明白了:

地址0x018F和地址0x0193处的代码分别将R7的值赋值为1和0,根据上面的解释可知,不论pin.b_1和pin.b_2为多少,程序都会执行0x018F这条指令——将R7赋值为1

其实R7中的值就是最后要求的pin.t_4的值,下面的代码只不过是把这个值放到pin这个字节变量中去——还真是麻烦啊!!!

所以 ,最终的结论就是 :不管pin.b_1和pin.b_2的值是多少,pin.t_4的值一定是1。

所以,我认为这是编译器的BUG,各位应同意吧。

赶快联系KEIL公司,要奖金去喽:lol

使用特权

评论回复
32
XIANSir|  楼主 | 2011-2-28 22:59 | 只看该作者
晚了,睡觉去了,明天继续听听坛上各位高手的见解。

不过说实话:我很心虚——想我这种菜鸟,能发现KEIL的BUG??:$

Good Night!:lol

使用特权

评论回复
33
linqing171| | 2011-2-28 23:01 | 只看该作者
假设
ACC     DATA    0E0H
那么二进制代码 E5E0 反汇编会有两种:
1 MOV A,#ACC
2 MOV A,0E0H
都是对的.

使用特权

评论回复
34
linqing171| | 2011-2-28 23:11 | 只看该作者
刚仔细看了一下楼主的反汇编.
keil的帮助中找了一通,没有发现~说不能往逻辑型号转.
确实是bug, 不知道有没有人用其他版本的编译一下看看有没有问题.

使用特权

评论回复
35
XIANSir|  楼主 | 2011-3-1 08:15 | 只看该作者
假设
ACC     DATA    0E0H
那么二进制代码 E5E0 反汇编会有两种:
1 MOV A,#ACC
2 MOV A,0E0H
都是对的.
linqing171 发表于 2011-2-28 23:01


这个我明白,但是MOV A,#ACC(0xE0)这种写法好像就比较奇怪了吧,括号是什么意思,是注释吗??#ACC(0xE0)这种写法真得是很怪异

使用特权

评论回复
36
ayb_ice| | 2011-3-1 08:28 | 只看该作者
XIANSir不要轻易怀疑什么这个编译器有BUG,那个编译器有BUG,当然不能说KEIL就没有BUG
必竟KEIL一向是很严谨的,而且历史悠久,能写编译器的人不是一般人
位变量可以使用~这个,不代表这样就是合理的,合法的
一个东西用了没有错,并不代表这就是正确的,因为你这只是一个情况而已
前面我已经说过了,类似的情况我在IAR上也有遇到,并且结果更糟糕,看反汇编都有产生代码,但却没有执行
建议用其它的51编译器试试,比较一下,比如IAR,GCC....

使用特权

评论回复
37
XIANSir|  楼主 | 2011-3-1 09:00 | 只看该作者
36# ayb_ice
谢谢前辈的提醒。
但我希望您能明白:我的困惑其实主要来自于1楼给出的那八中情况的不一致性,我希望您能够仔细对比那八幅图片的代码。

比如对比第一张图片和第三张图片,我们可以抽象出下面的问题:

变量1 = 表达式1
变量2 = 表达式2

(变量1 && 变量2)==((表达式1) && (表达式2))  这是恒等式吗???

我认为这应该是恒等式,如果哪一个C语言编译器使得这个恒等式不成立的话,那就应该是BUG。不知前辈认可否

使用特权

评论回复
38
刘前辈| | 2011-3-1 09:01 | 只看该作者
支持36楼,Keil是德国人,德国人做事的态度世人皆知。这种情况我从来认为是自己程序的缺陷造成的,把问题找出来,我就进步了。如果什么事先把错误原因推到别人身上,那永远不会进步。

LZ最好还是先沉下心来仔细找找自己的BUG。至少给自己一个说法。德国人、中国人,谁该向谁说对不起?

使用特权

评论回复
39
XIANSir|  楼主 | 2011-3-1 09:13 | 只看该作者
本帖最后由 XIANSir 于 2011-3-1 09:16 编辑

最新进展:我用VC++2008快速版对上面的代码进行了测试,发现结果与KEIL一致,图片如下:


也许C语言就是这样规定的,但是实在想不通为什会是这样子的。
希望对C语言有深入研究的可以帮忙解释一下,实在搞不懂其中的道理。

使用特权

评论回复
40
XIANSir|  楼主 | 2011-3-1 09:24 | 只看该作者
38# 刘前辈

刘前辈言重了!
我并没有非要让什么人认错的意思。只不过对于1楼的现象实在不理解(毕竟俺还很菜),所以发帖希望有高人能够为我解惑。

但是至今也没有人给出一个让我信服的解释,而且通过反汇编发现产生的汇编代码也确实不太符合常理——我是这样认为,你不妨也看看31楼的汇编解释,看有没有问题

再者说,如果真的能够解决这个现象(不是问题,是现象),那么我们都能得到进步,这不就是我们来论坛的目的吗??

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则