apcs

来源:互联网 发布:sublime text 3 js 编辑:程序博客网 时间:2024/06/13 02:15


APCS:调用规范

                             主要说明:sp, lr, pc ,fp 之间的关联关系。




参照示例说明:



为了对应CPU的使用情况,我们对这个办公台的使用做一些规定:

  1. 打开抽屉拿文件比打开文件架拿文件要快(寄存器的访问速度比堆栈快)
  2. 在文件架上存放文件只能由上往下依次存放,第14个抽屉(编号r13)里记录了目前存放到第几个格子。往文件架多放一张A4纸文件,第14个抽屉里的数字减一,从文件架里拿出来一张A4纸文件,第14个抽屉里的数字加一。(就是堆栈的使用要求)
  3. 第16个抽屉指定下一个使用办公台的是哪个员工。(就是PC寄存器的作用)
  4. 一个员工的名字要4张A4纸来写,这名字也太长了吧!情节需要,情节需要而已。(ARM指令是32位的,占4个字节)

 

我们来看看一群人共用这个办公台会出现什么问题。

第一个出现的问题就是下一个该由谁使用办公台的问题。

比如A员工使用了这张办公台,使用了一定的时间后在第16个抽屉(R15,PC)中放了个纸条,写了员工B的名字。按照使用规定,员工B会叫来使用这张办公台。员工B使用完办公台以后,如果有需要,他当然可以指定由员工C来使用这个办公台。如果他没有需要呢,就是员工B使用完办公台以后就不需要给其他员工了,这时办公台该交给谁使用呢?一种合理的做法是谁交给员工B的就交回给谁,在上面的情况中就是员工A了。不过员工B并不知道上一个使用者是谁,所以员工B使用了办公台以后就不知道该给谁了。随便叫一个过来?计算机世界里可没有随便的概念,说不定叫出个恐龙来。

解决这个问题的方法是做一个约定,上一个使用者在第15个抽屉(R14,lr)里放上自己的名字,让下一个使用者知道办公台应该交回给谁使用。

好,时光倒流,再来一遍:

A员工使用了这张办公台,使用了一定的时间后先在第15个抽屉(R14,lr)里放上自己的名字,就是“A员工”,然后在第16个抽屉(R15,PC)中放个纸条,写了员工B的名字。按照使用规定,员工B会叫来使用这张办公台。员工B使用完办公台以后,要交还给上一个使用者了,只需要将第15个抽屉(r14,lr)里的名字放入第16个抽屉(r15, pc)就好了。“A员工”的名字被放入了第16个抽屉,按照规定,A员工被叫来使用办公台。搞定!

员工B对应的ARM汇编代码就是:

 mov pc,lr  @将lr寄存器里的值拷贝到pc寄存器中。

 

第二个问题是第15个抽屉重复使用的问题。

对于上面情况,问题是否解决了呢,其实不是。

来看看,办公台由员工A交给了员工B,这时第15个抽屉(R14,lr)中是“A员工”的名字,如果员工B使用了以后需要交给员工C呢?按照约定,员工B需要在第15个抽屉(R14,lr)中先放上自己的名字“B员工”,然后再叫员工C过来。可是,抽屉里不是放着“A员工”的名字吗?

简单的解决方法是先把“A员工”的名字放到旁边抽屉里,旁边不是还有一溜抽屉嘛。

好,时光再次倒流,再来一遍:

A员工使用了这张办公台,使用了一定的时间后先在第15个抽屉(R14,lr)里放上自己的名字,就是“A员工”,然后在第16个抽屉(R15,PC)中放个纸条,写了员工B的名字。按照使用规定,员工B会叫来使用这张办公台。

员工B使用办公台的时候先将写着“A员工”的纸放到第1个抽屉里(r0),然后在第15个抽屉(R14,lr)里放上自己的名字“B员工”,最后在第16个抽屉(R15,PC)里放上员工C的名字,将办公台交给员工C使用。等员工C将办公台交回给员工B之后,员工B可以将第1个抽屉(r0)里的纸条(写着“A员工”)放回第16个抽屉(R15,PC)里就可以了。

员工B对应的ARM汇编代码如下:

mov r0,lr

mov lr,”返回地址”

mov pc,”子函数方法地址”

mov lr, r0

mov pc , lr

 

 

第三个问题是抽屉的使用权问题。

聪明的同学们很快会发现上面的问题,员工B在第1个抽屉保留了写着“A员工”的纸条,然后将办公台交给了员工C使用,员工C可不知道第1个抽屉装着什么,说不定一不小心就第1个抽屉的东西到了。

这里就有一个抽屉使用权的问题,让我们再做一些约定:第1个抽屉到第4个抽屉(r0~r3)大家可以随便使用,不管里面有什么东西,要用的时候将抽屉清空就好了;而第5个抽屉到第12个抽屉(r4~r11)里的东西不能乱动,交到你手里什么样子,交回给上一个使用者时就应该是什么样子。

本来觉得有了这些约定就安逸了,第1个到第4个抽屉不能放重要的东西,会被别人扔掉的,那就将重要的东西放第5个到第12个抽屉吧,别人会确保我的抽屉是原样的。仔细一想就有问题了,我要是使用第5个到第12个抽屉,我将办公台交回给上一个使用者的时候这些抽屉就不是原来的样子了,交不了差。

 

如果这时候再来时光倒流。。。。

A员工一上来就傻眼了,办公台上写着:第1个抽屉到第4个抽屉会被人倒掉,第5个抽屉到第12个抽屉是人家的东西,不能动。什么破公司,A员工骂一句就要交辞职报告了。

不要冲动,不要冲动,旁边不是还有个文件架嘛!既然第5个抽屉到第12个抽屉是人家的东西,你使用前先将抽屉里的东西搬到文件架上,等你用完了抽屉,将东西从文件架上搬回来就好了嘛。

 

留住了A员工,再来次时光倒流:

A员工开始使用这张办公台,他很规矩地将第5个抽屉到第12个抽屉里的东西搬到了文件架上,而且按规定在第14个抽屉(r13,SP)里记录了当前的文件架使用位置,在第15个抽屉(R14,lr)里放上自己的名字,就是“A员工”,然后在第16个抽屉(R15,PC)中放个纸条,写了员工B的名字。按照使用规定,员工B会叫来使用这张办公台。

员工B使用办公台的时候也是很规矩地将第5个抽屉到第12个抽屉里的东西搬到了文件架上,而且按规定在第14个抽屉(r13,SP)里记录了当前的文件架使用位置,接着将写着“A员工”的纸放到第5个抽屉里(r4),然后在第15个抽屉(R14,lr)里放上自己的名字“B员工”,最后在第16个抽屉(R15,PC)里放上员工C的名字,将办公台交给员工C使用。

等员工C将办公台交回给员工B之后,员工B可以将第5个抽屉(r4)里的纸条(写着“A员工”)放到第1个抽屉(r0),然后将A员工的东西从文件架上搬回到第5个抽屉到第12个抽屉里,最后是将第1个抽屉(r0)里的纸条(写着“A员工”)放回第16个抽屉(R15,PC)里就可以了。

好晕呀,真难为这些员工了,使用这种办公台的员工伤不起呀!操作寄存器的人伤不起呀!

员工B对应的ARM汇编代码如下:

sub sp,sp, #12 @开辟三个寄存器的空间,存放r4,r5,r6,样例而已,r7之后的就不存了

 

str r4, [sp] @r4入栈

str r5, [sp, #4]  @r5入栈

str r6, [sp, #8]   @r6入栈

@ 后面应该还有r7,r8…等等地操作,这里省略

 

mov r4,lr @ 将返回地址保留在r5中,因为r5不会被子函数破坏。

 

@调用子函数

 

Mov r0,r4 @将r4中的返回地址放到r0中,因为要开始恢复r4,r5,r6了

ldr r6, [sp, #8] @r4出栈

ldr r5, [sp, #4] @r5出栈

ldr r4, [sp]     @r6出栈

 

mov pc , r0 @将r0中的返回地址给pc,退出

 

 

第四个问题是文件架使用位置的问题。

上面的使用方法还是有问题呢! 员工A从员工B那里接手办公台就怒了:“我在第14个抽屉记录的文件架使用位置怎么不对呀”,原来员工B使用了第14个抽屉,忘了将第14个抽屉的恢复原样了。

于是又多了一条约定,第14个抽屉(R13,SP)里的东西也要保持原样。

这就有点麻烦了,第14个抽屉(R13,SP)记录的是文件架的使用位置,一旦使用文件架就得使用第14个抽屉(R13,SP),现在又要保留第14个抽屉的东西,这不是难为人吗?

 

想来想去,员工们想了个办法,使用的时候将第14个抽屉(R13,SP)的东西也搬到文件架上,不过有个问题,是先搬东西还是先做记录呢,怎么做都有问题。再看看抽屉,好像只剩第13个抽屉可以用了:

第1个抽屉到第4个抽屉(r0~r3):虽然是可以随便用,不过说不定上个人留了什么东西我自己需要的呢,先不动

第5个抽屉到第12个抽屉(r4~r11):是别人的东西,一会要搬到文件架上的

第14个抽屉:记录的就是文件架的使用位置

第15个抽屉:上一个使用者的名字

第16个抽屉:不能乱动的,叫别人过来的时候才用

 

好,就先将第14个抽屉的东西放到第13个抽屉上,然后在第14个抽屉记录新开了个文件架的格子,再将第13个抽屉的东西搬到新开的文件架格子上。为了方便,第15个抽屉的东西也搬到文件架上,剩下的就是依次将第5个抽屉到第12个抽屉的东西搬到文件架上。当使用完了再按顺序将文件架的东西搬回来就好了。

时光倒流就不来了,再来要吐了,对应ARM汇编代码如下:

mov ip,sp

sub sp,sp, #12

 

str lr, [sp]

str ip, [sp, #4]

str fp, [sp, #8]

sub fp, ip, #4

@子函数代码

ldr lr, [fp, #-8]

ldr ip, [fp, #-4]

ldr fp, [fp,#0]

mov sp, ip

mov pc , lr

 

对着一堆的抽屉讲了一通,大家可能看地时候好像整明白了,回过头看汇编代码又忘了。上面的例子只是希望通过形象的方式让大家理解,最终能在脑海里方便记忆的还是汇编代码和对应的注释。

@----------------------------------------------------

@程序刚开始,

@r0到r3(a1到a4)可能会有参数,不能动

@r4到r11(v1到v8)是父函数的东西,需要保留,不能动

@r13(SP)是堆栈指针

@r14(lr)是返回地址

@r15(PC)更是不能随便动能

 

mov ip,sp @将Sp(堆栈指针,r13)放在ip中,ip就是r12,目前唯一可以使用的寄存器

sub sp,sp, #12 @ 堆栈指针减12,开出相当于3个寄存器空间的堆栈。

 

str lr, [sp] @r14入栈,里面是返回地址

str ip, [sp, #4] @ r12入栈,里面是父函数的堆栈指针

str fp, [sp, #8] @ r11入栈,里面是需要为父函数保留的东西,一会要用r11,所以将它入栈

 

sub fp, ip, #4  @ r11(就是fp)等于ip+4,就是自己的栈底,以后可以方便地使用堆栈

 

@添加自己的代码

 

@下面是准备退出了,注意堆栈使用时是基于fp寻址的,

@因为此时SP可能在自己的代码中调整过,不可靠。

ldr lr, [fp, #-8] @ lr出栈,里面是返回地址

ldr ip, [fp, #-4] @ ip出栈,里面是父函数的堆栈指针

ldr fp, [fp,#0]   @fp出栈,里面是需要为父函数保留的东西

mov sp, ip    @令sp等于ip,帮父函数恢复堆栈指针

mov pc , lr    @令pc等于lr,就是等于返回地址,退出子函数,返回到父函数。

 @------------------------------------

 

最后很开心地告诉大家,以上的分析和代码都只是为了让大家理解APCS,其实大家在使用过程中只要按模式在过程(或者说是函数)开始和结束的时候加上标准的APCS代码就好了。下面是简化过的APCS标准代码,有兴趣的同学可以研究一下,没兴趣研究的话也没有问题,写一个过程的时候直接拷贝上去就好了。

mov ip,sp

stmfd sp!, {fp,ip,lr,pc}

sub fp, ip, #4

@......子函数代码

sub sp, fp, #12

ldmfd sp,{fp, sp, pc}


1 0