arm堆栈操作DOC.docx

上传人:b****6 文档编号:12560462 上传时间:2023-06-06 格式:DOCX 页数:9 大小:18.76KB
下载 相关 举报
arm堆栈操作DOC.docx_第1页
第1页 / 共9页
arm堆栈操作DOC.docx_第2页
第2页 / 共9页
arm堆栈操作DOC.docx_第3页
第3页 / 共9页
arm堆栈操作DOC.docx_第4页
第4页 / 共9页
arm堆栈操作DOC.docx_第5页
第5页 / 共9页
arm堆栈操作DOC.docx_第6页
第6页 / 共9页
arm堆栈操作DOC.docx_第7页
第7页 / 共9页
arm堆栈操作DOC.docx_第8页
第8页 / 共9页
arm堆栈操作DOC.docx_第9页
第9页 / 共9页
亲,该文档总共9页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

arm堆栈操作DOC.docx

《arm堆栈操作DOC.docx》由会员分享,可在线阅读,更多相关《arm堆栈操作DOC.docx(9页珍藏版)》请在冰点文库上搜索。

arm堆栈操作DOC.docx

arm堆栈操作DOC

arm堆栈操作

arm堆栈的组织结构是满栈降的形式,满栈即sp是要停留在最后一个进栈元素,降:

就是堆栈的增长方向是从高地址向低地址发展。

 arm对于堆栈的操作一般采用LDMFD(pop)和STMFD(push)两个命令。

以前困惑的就是STMFD命令对于操作数是按照什么顺序压栈的

比如:

STMFDsp!

{R0-R5,LR}进栈顺序是:

高地址(1方式)

LR

R5

R4

```````

R0  <-sp

低地址

还是:

高地址(2方式)

R0

R1

```

R5

LR<-sp

低地址

现在通过下表,可以轻松的解决这个问题:

寻址方式

说明

pop

=LDM

push

=STM

FA

递增满

LDMFA

LDMDA

STMFA

STMIB

FD

递减满

LDMFD

LDMIA

STMFD

STMDB

EA

递增空

LDMEA

LDMDB

STMEA

STMIA

ED

递减空

LDMED

LDMIB

STMED

STMDA

按照图表,可知STMFD对应的是STMDB,根据arm指令手册,可知STMDB入栈顺序是(1方式)

而LDMFD对应的是LDMIA,这样这两个操作就可以成功配对:

 

以下是我在学习ARM指令中记录的关于堆栈方面的知识

1、寄存器R13在ARM指令中常用作堆栈指针

  2、对于R13寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。

采用以下的记号来区分不同的物理寄存器:

R13_其中,mode为以下几种模式之一:

usr、fiq、irq、svc、abt、und。

  3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。

而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。

由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。

  4、有四种类型的堆栈:

  堆栈是一种数据结构,按先进后出(FirstInLastOut,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。

  当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(FullStack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(EmptyStack)。

  同时,根据堆栈的生成方式,又可以分为递增堆栈(AscendingStack)和递减堆栈(DecendingStack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。

这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:

      ◎Fulldescending满递减堆栈堆栈首部是高地址,堆栈向低地址增长。

栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。

ARM-Thumb过程调用标准和ARM、ThumbC/C++编译器总是使用Fulldescending类型堆栈。

<这是什么原因呢?

>

  ◎Fullascending满递增堆栈堆栈首部是低地址,堆栈向高地址增长。

栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。

  ◎Emptydescending空递减堆栈堆栈首部是低(这里是不是错了,应该是高地址吧)地址,堆栈向高地址增长。

栈指针总是指向下一个将要放入数据的空位置。

  ◎Emptyascending空递增堆栈堆栈首部是高地址,堆栈向低地址增长。

栈指针总是指向下一个将要放入数据的空位置。

  5、操作堆栈的汇编指令堆栈类型入栈指令出栈指令FulldescendingSTMFD(STMDB)LDMFD(LDMIA)FullascendingSTMFA(STMIB)LDMFA(LDMDA)EmptydescendingSTMED(STMDA)LDMED(LDMIB)EmptyascendingSTMEA(STMIA)LDMEA(LDMDB)

  例子:

STMFDr13!

{r0-r5};PushontoaFullDescendingStackLDMFDr13!

{r0-r5};PopfromaFullDescendingStack.

 

例子

1)保护现场参数,不影响PC,嵌汇编的时候对之前的存参数的寄存器R0~R12保存

STMFDr13!

{r0-r7,LR}

LDMFDr13!

{r0-r7,PC}

2)ARM汇编中lr(r14)寄存器的作用

lr(r14)的作用问题,这个lr一般来说有两个作用:

1.当使用bl或者blx跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复。

2.异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址。

另外注意pc,在调试的时候显示的是当前指令地址,而用movlr,pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用movpc,pc,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果.

3.我们看到的LR值是上一个子程序调用保存的子程序返回地址,这个LR是要赋给PC的。

嵌入式汇编要手动保存返回地址,进行现场保护。

PC记录当前运行的地址。

下一条回自己+4

进入子程序,LR才自动更新为返回地址值,PC为程序运行地址

 

ARM汇编嵌套子程序

几个星期前阅读了(加)CarlHamacher、ZvonkoVranesic、SafwatZaky编写的《计算机组成》第五版中的ARM子程序调用的一些知识,启发很大,顺便将它整理了一下并加入了自己的理解。

子程序

1通过寄存器传递参数

BL指令通常用于调用一个子程序。

它和B指令的区别在于它将返回地址装载到R14中。

由于子程序可能是嵌套的,因此LR的内容必须保存在子程序所使用的堆栈中。

下面的例子使用寄存器传递参数。

调用者通过寄存器R1和R2分别将数组的大小和数组的首地址传递给子程序;子程序利用寄存器R0将和传递给调用者。

该子程序使用了寄存器R3,必须将它和LR推入堆栈。

调用程序

LDRR1,N

LDRR2,POINTER

BLLISTADD

STRR0,SUM

子程序

LISTADDSTMFDR13!

{R3,R14}

MOVR0,#0

LOOPLDRR3,[R2],#4

ADDR0,R0,R3

SUBSR1,R1,#1

BGTLOOP

LDMFDR13!

{R3,R15}

注:

这里并没有遵守APCS(ARM过程调用标准),一般由调用者负责保存R0~R3,被调用者负责保存其他的寄存器以使调用返回后程序的状态不被破坏。

2通过堆栈传递参数

调用程序

LDRR0,POINTER

STRR0,[R13,#-4]!

;将数组首地址推入堆栈

LDRR0,N

STRR0,[R13,#-4]!

;将元素个数N推入堆栈

BLLISTADD

LDRR0,[R13,#4];将元素和装载到寄存器R0中

STRR0,SUM

ADDR13,R13,#8;恢复堆栈

子程序

LISTADDSTMFDR13!

{R0-R3,R14}

LDRR1,[R13,#20]

LDRR2,[R13,#24]

MOVR0,#0

LOOPLDRR3,[R2],#4

ADDR0,R0,R3

SUBSR1,R1,#1

BGTLOOP

STRR0,[R13,#24];把和推入堆栈的最深处

LDMFDR13!

{R0-R3,R15}

 

[R0]

[R1]

[R2]

[R3]

返回地址

N

POINTER/SUM

3嵌套子程序

当子程序嵌套时,堆栈是用于处理返回地址的最合适的数据结构。

当调用子程序时在堆栈上建立了完整的堆栈结构。

应当注意当前子程序的堆栈帧指针所指向的空间中存储的是调用当前子程序的子程序的堆栈帧指针。

调用者将子程序所需要的参数按照顺序推入堆栈。

子程序首先保存工作寄存器、调用者的堆栈帧指针以及返回地址,然后它计算自己的堆栈帧指针的值(ADDFP,SP,#16),并利用这个堆栈帧指针从堆栈帧中获取调用者传递给它的参数。

在子程序完成它的任务之后,它也将返回值保存在堆栈中,此例保存在参数所在的内存单元。

调用者和被调用者必须约定好参数的传递顺序和返回值保存位置。

如果返回值比较多的话,调用者要为返回值预先在堆栈中保留合适的空间。

调用程序

2000LDRR0,PARAM2

STRR0,[SP,#-4]!

;将参数推入堆栈

LDRR0,PARAM1

STRR0,[SP,#-4]!

BLSUB1

2020LDRR0,[SP];保存SUB1的结果

STRR0,RESULT

ADDSP,SP,#8;恢复堆栈

子程序

2100SUB1STMFDSP!

{R0-R3,FP,LR}

ADDFP,SP,#16;计算帧指针

LDRR0,[FP,#8];载入参数1

LDRR1,[FP,#12];载入参数2

LDRR2,PARAM3;载入参数3

STRR2,[SP,#-4]!

;将参数3推入堆栈

BLSUB2

2164LDRR2,[SP],#4;将SUB2的结果弹出并存储在R2中,并递增SP

STRR3,[FP,#8];将结果推入堆栈

LDMFDSP!

{R0-R3,FP,PC};恢复寄存器并返回

3000SUB2STMFDSP!

{R0,R1,FP,LR}

ADDFP,SP,#8;载入结构指针

LDRR0,[FP,#8];载入参数

STRR1,[FP,#8];将结果推入堆栈

LDMFDSP!

{R0,R1,FP,PC}

 

[R0]fromSUB1

[R1]fromSUB1

[FP]fromSUB1

2164/返回地址

param3/SUB2的结果最后保存在这里

[R0]frommain

[R1]frommain

[R2]frommain

[R3]frommain

[FP]frommain

2020/返回地址

param1/SUB1的结果最后保存在这里

param2

原栈顶

 

 

 

3子程序编译后都放在哪

嵌汇编子程序:

会在编译后统一放到一个地方系统调用__main()库函数,

后再进入main.c前的初始化C下面的堆栈命令前(这个堆栈是用来放置C里参数的)

嵌汇编子程序:

按定义顺序放在堆栈前,而C语言子程序,放在初始化堆栈堆栈命令后。

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2