请教AVR程序跳转相关的汇编指令的问题

2019-03-24 20:26发布

最近在弄AVR的汇编,遇到了些问题,麻烦热心的网友帮忙看看是怎么回事。

手上的开发板是atmega128a,编译环境是IAR FOR AVR 5.40,仿真器是jtagice。用C编写的代码可以正常编译通过、下载、调试。
用io驱动led,使用定时器中断等都是没有问题的。

阅读了atmega128a的datasheet和8位avr汇编指令的手册,都是atmel官方的。
但是在学习汇编指令时遇到了问题,以下是具体描述:

首先是call指令的问题。
在调试窗口下记录了以下信息:
PC = 0x000FE;//不知道为什么不是16位的,多1个零,很奇怪。先不管,接着往下看。
SP = 0x013D;
当前栈中的数据如下:
(0x013F) = 0x92;(0x013E) = 0x02;(0x013D) = 0xE0;(0x013C) = 0x02;(0x013B) = 0xAE;(0x013A) = 0x02;

CALL function后:
跳转到对应的function中,没问题。然后栈中,(0x013D) = 0x81;(0x013C) = 0x00;
PC = 0x00138;
SP = 0x013B;

在官方手册中看了CALL的描述,操作流程是:
1).把PC + 2 这个值保存到STACK中
2) .SP = SP -2
3). 目标地址写入PC中,程序跳转

因为AVR的栈是空递减的栈,所以,应该是(0x013D,0x013C) = 0x000FE + 2 = 0x00100;
不知道是(0x013D) = 0x00,(0x013C) = 0x01;还是(0x013D) = 0x01,(0x013C) = 0x00;//其实最开始就是为了研究这个问题的,结果却发现了一堆子问题。
手册真不给力,找了半天没找到,干脆就直接在调试环境下自己验证了,没想到卡在这些问题上了。

而现在的实际情况是SP = 0x013B,这个是没问题的,之前的0x013D - 2 = 0x013B。
但是这个栈里面的0x00 ,0x81就不知道是怎么出来的,根本就没见到0x0100的影子,0x00,0x81到底是怎么冒出来的。
可恨的是,后面插入RETI指令,它居然还能正常返回到之前那个函数的下一句。
这说明0x00,0x81是没问题的,另外,CALL和RETI对栈中PC的存取是对应的,不然就不会正常返回。所以我只需要研究其中一种就知道另一种的顺序了。

RETI的就不列举了。 此帖出自小平头技术问答
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
4条回答
lr2131
1楼-- · 2019-03-25 02:50
哈哈,刚发完贴,没一会就发现了一些头绪,但是还没有完全搞透,也没有看到文档上类似的说法,就先不贴出来了。待后续研究和验证通过后,再贴出答案。

0x0100 和0x0081是有某种关联的,类似的还有一些数据
0x013C和0x009F;
0x00CE和0x0067;
0x00E6和0x0073;
lr2131
2楼-- · 2019-03-25 08:20
或许是因为汇编现在使用的越来越少的缘故,关注汇编和底层的人越来越少。
看帖的人不少,回帖的人却没有,确实有点让人的积极性受打击。

以下将给出关于该问题的分析结果。
但申明,这里不保证我给出结论的准确性,因为得到的结论在官方手册上没有看到,仅仅通过了实验论证,也就是说按照这个的理解,程序确实正常的在运行中。

目前在AVR上写出的简单任务调度器,调度器可以正常运行,只是有点其他的小问题。

分析结果:
1.首先手册上说到,CALL 指令时,讲PC+2压入栈中,没错。但是我理解错了,认为是当前的PC+2,。
但其实应该是PC+2+2压入栈中,为什么是这样,我也说不清楚,可能是因为AVR是一级流水线的缘故,和ARM3级流水线有些类似的问题。也可能是其他原因,真心说不上来。

2.所以上面计算的0x0100 错了,应该是0xFE+2+2 = 0x102,
另外在程序跟踪函数地址时,0x013C算错了,同上也应该是0x013E。

好,那么以下四组值的关系
0x0102 和0x0081;
0x013E和0x009F;
0x00CE和0x0067;
0x00E6和0x0073;

都是前面的数都是后面数的2倍,或者这么说,前面的数经过右移一位后变成后面的数。

这些数值在C语言中是函数首地址,在汇编中说是某段汇编程序的首地址。
因为AVR的指令都是2字节的,或者是4字节的,我也不知道有没有3字节的。
也就是说,如果没有奇数字节的话就能解释通了,如果没有奇数字节的话,每条指令的地址都是2字节对齐的,
那么地址在最低位都是0,所以可能AVR为了节省位,在CALL等指令时把地址的高15bit全部压栈,同时移除最低位,因为这个位没有用,都是0嘛,然后在RETI或REI等指令把地址弹栈到PC中时,又做了反向的操作,把15bit的数据弹出来,放到PC的高15bit中,而最低位仍然是0。
容若思雪
3楼-- · 2019-03-25 11:47
额,或许我能解决你的疑问。首先跳转的时候为什么是PC=PC+2+2。。
     你是知道的AVR能够实现一级流水-指令预取。也就是执行当前指令的时候预先读取下一条指令,那么PC中保存的是预取的指令。当你执行call的时候,是不会立即跳转的,它是先执行完PC指针中的指令,然后入栈,入栈完毕,进行跳转。入栈也就是取下一条指令。当他执行完PC指向的指令的时候PC就自动+2了。然后取下条指令入栈再+2也就是这样了。你为什么要执行完PC指向的指令才能入栈呢?很明显那条指令是预取的如果直接跳转,那么就相当于只是预取了并没有执行。这是我的理解

再说为什么SP中保存的数据时PC的1/2。这个也很好理解。你是知道的AVR指令双字节对齐。有四字节指令多数是长跳转指令-高字是跳转指令,低字是跳转到的地址。无论怎样都是双字节对齐。根据手册的解释。PC的6-0位表示页内地址。每页128字,256字节。对于AVR64有256个页,那么14-7表示页地址。虽然是6个位地址空间,但是累加起来正好满足。128的Flash扩展使用到了rampz。屏蔽掉rampz之后和64一样。再说为什么是双倍的关系。,PC中始终保存的是字地址,所以入栈之后你看到的就是字地址。但是你仿真读取出来的是字节地址。是编译环境显示的字节地址。。

OK,我觉得我的解释还算合理。看了手册之后也没什么纰漏。
容若思雪
4楼-- · 2019-03-25 12:05
额,其实我是在找AVR的short  branch的跳转范围是多少的。看到了你的帖子,就研究了一会儿。。。PS:我使用的是ICC,ICC的调用是使用软件堆栈。没有那么直接,另外,我也没有仿真器,所以只能理论推理。

一周热门 更多>