arm堆栈操作(DOC)
- 格式:doc
- 大小:61.00 KB
- 文档页数:16
GNUARM汇编指令---.word第⼀部分 Linux下ARM汇编语法尽管在Linux下使⽤C或C++编写程序很⽅便,但汇编源程序⽤于系统最基本的初始化,如初始化堆栈指针、设置页表、操作 ARM的协处理器等。
初始化完成后就可以跳转到C代码执⾏。
需要注意的是,GNU的汇编器遵循AT&T的汇编语法,可以从GNU的站点()上下载有关规范。
⼀. Linux汇编⾏结构任何汇编⾏都是如下结构:[:] [} @ comment[:] [} @ 注释Linux ARM 汇编中,任何以冒号结尾的标识符都被认为是⼀个标号,⽽不⼀定⾮要在⼀⾏的开始。
【例1】定义⼀个"add"的函数,返回两个参数的和。
.section .text, “x”.global add @ give the symbol add external linkageadd:ADD r0, r0, r1 @ add input argumentsMOV pc, lr @ return from subroutine@ end of program⼆. Linux 汇编程序中的标号标号只能由a~z,A~Z,0~9,“.”,_等字符组成。
当标号为0~9的数字时为局部标号,局部标号可以重复出现,使⽤⽅法如下:标号f: 在引⽤的地⽅向前的标号标号b: 在引⽤的地⽅向后的标号【例2】使⽤局部符号的例⼦,⼀段循环程序1:subs r0,r0,#1 @每次循环使r0=r0-1bne 1f @跳转到1标号去执⾏局部标号代表它所在的地址,因此也可以当作变量或者函数来使⽤。
三. Linux汇编程序中的分段(1).section伪操作⽤户可以通过.section伪操作来⾃定义⼀个段,格式如下:.section section_name [, "flags"[, %type[,flag_specific_arguments]]]每⼀个段以段名为开始, 以下⼀个段名或者⽂件结尾为结束。
arm函数调用中的堆栈变化在计算机的运行过程中,函数调用是一种常见的操作。
当程序调用一个函数时,需要先将当前的运行状态(例如当前指令的地址、堆栈指针等)保存在堆栈中,然后跳转到函数中执行。
函数执行完毕后,再从堆栈中恢复之前的运行状态,继续执行原来的程序。
这个过程中,堆栈扮演了一个非常重要的角色。
本文将介绍在ARM架构中,函数调用时堆栈的变化。
1. 堆栈的基本概念在程序中,有一片内存区域被用来存放函数的局部变量和一些临时变量,称为堆栈。
堆栈是一个先进后出的数据结构,即最后存入的数据最先弹出。
当程序调用一个函数时,会在堆栈中分配一段空间来存放函数的参数、局部变量、返回地址等信息。
当函数返回时,这些信息会从堆栈中弹出,恢复程序之前的状态。
在ARM架构中,堆栈的地址是4字节对齐的,即堆栈指针(SP)的值必须是4的倍数。
这是因为ARM指令集中的大多数指令都是以4字节为单位的,如果SP不是4字节对齐的,那么执行指令时会出错。
2. 函数调用时堆栈的变化当程序调用一个函数时,堆栈的变化可以分为以下几个步骤:(1)保存寄存器在ARM架构中,函数调用过程中一些重要的状态信息通常保存在寄存器中。
为了不影响原程序的运行,需要在堆栈中保存这些寄存器的值。
这些寄存器包括:R0~R3(函数参数)、R14(LR,返回地址)、R13(SP,堆栈指针)、R11(FP,帧指针)等。
在进入函数之前,需要将这些寄存器的值压入堆栈中。
具体操作为:``` PUSH {R0-R3, R11, LR} ```以上指令将R0~R3、R11、LR的值压入堆栈中。
其中,LR保存的是返回地址,R11保存的是帧指针(Frame Pointer,FP)。
FP是一个指针,指向当前函数的栈帧,用于访问局部变量。
堆栈指针SP也需要被保存,但不是在这里保存,而是在进入函数之前保存。
(2)分配空间在堆栈中为当前函数分配空间,用于存放参数、局部变量和其它临时变量。
分配的空间的大小由当前函数所需的局部变量和参数决定。
51单片机堆栈操作指令的用法51单片机是一种非常常用的单片机芯片,其指令集非常丰富,其中包含了很多堆栈操作指令。
堆栈操作指令是用来进行数据的入栈和出栈操作的指令,通过堆栈操作指令,我们可以方便地保存和恢复程序执行中的临时数据,提高代码的灵活性和效率。
本文将详细介绍51单片机堆栈操作指令的用法,帮助读者更好地理解和运用这些指令。
一、堆栈简介堆栈(Stack)是一种特殊的数据结构,具有后进先出(LIFO)的特点。
在51单片机的内部RAM中,有一段专门用来存放堆栈的空间,这段空间的大小为128字节(地址为0x07Fh至0x080h)。
在程序执行过程中,我们可以通过堆栈操作指令将数据入栈或者出栈,进栈是将数据放入堆栈,出栈是将数据从堆栈中取出。
二、堆栈操作指令51单片机的指令集中包含了以下几条堆栈操作指令:1. PUSH 指令PUSH指令用于将8位数据入栈,将要入栈的数据放入寄存器A中,通过PUSH 指令可以将A的数据压入堆栈。
PUSH指令的实际操作是将A的数据先放入栈顶指针(SP)所指向的内存单元中,然后将SP的值减1,即栈顶指针向下移动一个位置。
2. POP 指令POP指令用于将数据出栈,即从堆栈中取出一个8位数据,并放入寄存器A中。
POP指令的实际操作是将栈顶指针向上移动一个位置,然后将栈顶指针所指向的内存单元中的数据取出,并放入A中。
3. XCH指令XCH指令用于交换A寄存器的数据和栈顶指针所指向的内存单元的数据。
具体操作是将栈顶指针所指向的内存单元中的数据取出,并放入A中,然后将A中的数据放回栈顶指针所指向的内存单元中。
4. LCALL指令LCALL指令是一个特殊的调用指令,用于将下一条指令的地址入栈,并转移到指定地址处执行。
具体操作是将下一条指令的地址(即当前指令的地址加3)入栈,然后将指定地址的值赋给程序计数器(PC)。
5. RET指令RET指令用于从子程序返回,从堆栈中取出地址,并赋给程序计数器(PC),从而实现返回到调用该子程序的地方继续执行。
1、寄存器R13 在ARM 指令中常用作堆栈指针2、对于R13 寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:R13_<mode>其中,mode为以下几种模式之一:usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。
而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
4、有四种类型的堆栈:堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(DecendingStack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。
这样就有四种类型的堆栈工作方式,ARM 微处理器支持这四种类型的堆栈工作方式,即:◎Full descending 满递减堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用Full descending 类型堆栈。
arm汇编栈指令Arm汇编栈指令是针对ARM处理器的汇编指令集中的一类指令。
ARM处理器是当今常见的一种芯片,在移动设备或者网络设备中使用广泛。
在ARM汇编语言中,栈指令被广泛应用,用来进行程序栈的操作。
本文将详细介绍ARM汇编栈指令,并分步骤介绍这类指令的使用。
一、什么是栈在程序设计中,栈是一种非常重要的数据结构。
它是一种特殊的数据结构,使用“后进先出”的原则来为操作提供支持。
在栈中,最后加入的数据项是第一个被取出的,而最先加入的数据项则是最后一个被取出的,这就是“后进先出”的原则。
同时,栈还支持两种基本操作PUSH和POP。
PUSH操作将数据项压入栈中,POP操作则是从栈中弹出最近压入的数据项。
二、栈指令的分类在ARM汇编语言中,栈指令通常分为两类: PUSH指令和POP指令。
PUSH指令用来把操作数压入栈中,而POP指令则是从栈中弹出操作数。
三、指令详解ARM处理器中的PUSH指令有以下几个指令:1. PUSH {registers}该指令会从指定的一组寄存器中把值压入到栈中。
例如,PUSH{r0-r3},就是把寄存器r0-r3中的值按递减的顺序压入到栈中。
2. PUSH {register_list}!该指令使用register_list中的寄存器将一个或多个寄存器的值压入到栈中。
在此指令中,感叹号!表示将程序栈指针(SP)向下移动。
而POP指令也有以下两个指令:1. POP {registers}该指令出栈指定的一组寄存器中的值,并将其写入寄存器。
例如,POP {r0-r3},就是从栈中递增取出值并将其写入寄存器r0-r3。
2. POP {register_list}!该指令从堆栈中出栈 register_list 中指定的寄存器,并将它们的值存储到 register_list 中指定的寄存器中。
四、使用方法在使用栈指令时,我们需要注意一些细节,下面我们以使用PUSH指令为例进行详细讲解。
堆栈和队列的基本操作一、堆栈(Stack)堆栈是一种具有特殊插入和删除规则的线性数据结构。
它按照“后进先出”(Last-In-First-Out, LIFO)原则管理数据。
1.堆栈的初始化堆栈的初始化即创建一个空堆栈。
2. 入栈(Push)入栈是将数据插入到堆栈顶部的操作。
数据插入后,堆栈的长度加1、插入的数据成为新的堆栈顶部。
3. 出栈(Pop)出栈是将堆栈顶部的数据删除的操作。
删除后,堆栈的长度减1、删除的数据为原堆栈的顶部。
4. 取栈顶元素(Top)取栈顶元素是获取当前堆栈顶部的数据,而不进行删除操作。
5. 判断堆栈是否为空(IsEmpty)判断堆栈是否为空,即判断堆栈的长度是否为0。
6. 获取堆栈长度(GetSize)获取堆栈的长度,即当前堆栈中元素的数量。
堆栈可以使用数组或链表来实现。
数组实现的堆栈称为顺序堆栈,链表实现的堆栈称为链式堆栈。
堆栈的应用:-递归函数的调用和返回-表达式求值-括号匹配-浏览器前进后退功能二、队列(Queue)队列也是一种具有特定插入和删除规则的线性数据结构。
它按照“先进先出”(First-In-First-Out, FIFO)原则管理数据。
1.队列的初始化队列的初始化即创建一个空队列。
2. 入队(Enqueue)入队是将数据插入到队列尾部的操作。
数据插入后,队列的长度加1、插入的数据成为新的队列尾部。
3. 出队(Dequeue)出队是将队列头部的数据删除的操作。
删除后,队列的长度减1、删除的数据为原队列的头部。
4. 获取队首元素(Peek)获取队列头部的数据,而不进行删除操作。
5. 判断队列是否为空(IsEmpty)判断队列是否为空,即判断队列的长度是否为0。
6. 获取队列长度(GetSize)获取队列的长度,即当前队列中元素的数量。
队列也可以使用数组或链表来实现。
数组实现的队列称为顺序队列,链表实现的队列称为链式队列。
还有一种特殊的队列称为优先队列,它根据元素的优先级进行排序。
ARM启动代码中堆栈初始化的解析在启动代码中有:;定义堆栈的大小SVC_STACK_LEGTH EQU 0FIQ_STACK_LEGTH EQU 0IRQ_STACK_LEGTH EQU 128ABT_STACK_LEGTH EQU 0UND_STACK_LEGTH EQU 0ResetLDR PC, ResetAddrLDR PC, UndefinedAddrLDR PC, SWI_AddrLDR PC, PrefetchAddrLDR PC, DataAbortAddrDCD 0xb9205f80LDR PC, [PC, #-0xff0]LDR PC, FIQ_AddrResetAddr DCD ResetInit UndefinedAddr DCD UndefinedSWI_Addr DCD SoftwareInterrupt PrefetchAddr DCD PrefetchAbort DataAbortAddr DCD DataAbortNouse DCD 0IRQ_Addr DCD 0FIQ_Addr DCD FIQ_Handler;未定义指令UndefinedB Undefined;软中断SoftwareInterruptB SoftwareInterrupt;取指令中止PrefetchAbortB PrefetchAbort;取数据中止DataAbortB DataAbort;快速中断FIQ_HandlerB FIQ_HandlerStackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4 StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4 StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4 StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4 StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4;/* 分配堆栈空间 */AREA MyStacks, DATA, NOINIT, ALIGN=2SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;中断模式堆栈空间FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;快速中断模式堆栈空间AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;中止义模式堆栈空间UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;未定义模式堆栈其中,IRQ_STACK_LEGTH 为128,根据IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;中断模式堆栈空间可知,IrqStackSpace 为128*4个字节空间的首地址,然后在StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4 中, IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4 = 128*4 + (128-1)*4 = (128+127)*4,就是说实际的irq堆栈大小为 (128+127)*4个字节,是这样的吗?回答1:是这样吗?StackIrq DCD IrqStackSpace +(IRQ_STACK_LEGTH - 1)* 4中的IrqStackSpace 为128*4个连续字节的首地址,IrqStackSpace +(IRQ_STACK_LEGTH - 1)* 4 表示一个基址加上一个内存大小的偏移量从而构成了128*4 个连续的字节空间?回答2:可如果是这样的话直接写为StackIrq DCD (IRQ_STACK_LEGTH )* 4不就可以了吗?为什么还要用到space语句呢?回答3:不是你讲的那样StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4这里的StackIrq是顶格写的,是一个标号,实质就是一个地址。
ARM堆栈分析ARM是一种处理器架构,常用于嵌入式系统和移动设备。
在ARM架构中,堆栈是一种用于管理函数调用和局部变量的重要数据结构。
本文将对ARM堆栈进行详细分析。
首先,我们来了解ARM堆栈的基本概念。
堆栈是一种后进先出(LIFO)的数据结构,用于存储函数调用时需要保存的信息。
在ARM架构中,堆栈是由系统自动管理的,不需要程序员手动操作。
在ARM中,堆栈使用堆栈指针(Stack Pointer,SP)来标识当前堆栈的位置。
当函数调用时,SP指针会被更新,将当前的函数调用信息(如返回地址和局部变量)压入堆栈。
当函数返回时,SP指针会被恢复,将之前保存的信息弹出堆栈。
堆栈帧是函数调用时在堆栈上分配的一块连续内存空间,用于存储函数的参数、局部变量和其他状态信息。
堆栈帧由以下几部分组成:1. 返回地址(Return Address):函数执行完毕后,需要返回到调用者继续执行。
因此,返回地址是堆栈帧中的一个重要字段。
2.参数和返回值:函数调用时,其参数会被压入堆栈。
返回值则会通过寄存器或堆栈传递给调用者。
3. 本地变量(Local Variables):函数执行过程中所需的变量空间会在堆栈上分配。
函数退出后,这些变量所占用的堆栈空间将被释放。
4. 寄存器保存(Register Saving):如果函数需要在执行过程中使用寄存器来保存临时变量等信息,那么在函数调用时,这些寄存器中的值需要保存在堆栈中。
在ARM架构中,堆栈的使用也非常重要。
ARM处理器包含一组寄存器,如程序计数器(Program Counter,PC)、堆栈指针(Stack Pointer,SP)和链接寄存器(Link Register,LR)。
在函数调用过程中,这些寄存器的值会被保存在堆栈中,以便函数返回时能够正确恢复。
在ARM中,堆栈的使用涉及到两个重要指令:PUSH和POP。
PUSH指令用于将寄存器值压入堆栈,而POP指令用于将堆栈中的值弹出到寄存器。
C语言及ARM中堆栈指针SP设置的理解与总结1什么是栈百度这么说:栈是一种特殊的线性表,是一种只允许在表的一端进行插入或删除操作的线性表。
表中允许进行插入、删除操作的一端称为栈顶。
表的另一端称为栈底。
栈顶的当前位置是动态的,对栈顶当前位置的标记称为栈顶指针。
当栈中没有数据元素时,称之为空栈。
栈的插入操作通常称为进栈或入栈,栈的删除操作通常称为退栈或出栈。
简易理解:客栈,即临时寄存的地方,计算机中的堆栈主要用来保存临时数据,局部变量和中断/调用子程序程序的返回地址。
程序中栈主要是用来存储函数中的局部变量以及保存寄存器参数的,如果你用了操作系统,栈中还可能存储当前进线程的上下文。
设置栈大小的一个原则是,保证栈不会下溢出到数据空间或程序空间.CPU在运行程序时,会自动的使用堆栈,所以堆栈指针SP就必须要在调用C程序前设定。
CPU的内存RAM空间存放规律一般是分段的,从地址向高地址,依次为:程序段(.text)、BSS段,上面还可能会有堆空间,然后最上面才是堆栈段。
这样安排堆栈,是因为堆栈的特点决定的,堆栈的指针SP初始化一般在堆栈段的高地址,也就是内存的高地址,然后让堆栈指针向下增长(其实就是递减)。
这样做的好处就是堆栈空间远离了其他段,不会跟其他段重叠,造成修改其他段数据,而引起不可预料的后果,还有设置堆栈大小的原则,要保证栈不会下溢出到数据空间或者程序空间。
所谓堆栈溢出,是指堆栈指针SP向下增长到其他段空间,如果栈指针向下增长到其他段空间,称为堆栈溢出。
堆栈溢出会修改其他空间的值,严重情况下可造成死机. 2堆栈指针的设置开始将堆栈指针设置在内部RAM,是因为不是每个板上都有外部RAM,而且外部RAM 的大小也不相同,而且如果是SDRAM,还需要初始化,在内部RAM开始运行的一般是一个小的引导程序,基本上不怎么使用堆栈,因此将堆栈设置在内部RAM,但这也就要去改引导程序不能随意使用大量局部变量。
堆栈操作方法讲解堆栈(stack)是一种经典的数据结构,它的特点是后进先出(LIFO,Last In First Out)。
堆栈可以看作是一种特殊的线性表,它只能在表的一端进行插入和删除操作。
具体来说,堆栈的插入操作通常称为“入栈”,删除操作称为“出栈”。
堆栈常用于需要临时存储数据的场景,例如函数调用、表达式求值、括号匹配等。
在计算机科学领域,堆栈也是一种非常基础的数据结构,几乎在各个领域都有着广泛的应用。
堆栈的实现可以通过数组或链表来完成。
下面我们将详细介绍堆栈的基本操作方法,包括创建堆栈、入栈、出栈、获取栈顶元素等。
1. 创建堆栈在实际应用中,我们可以使用数组或链表来实现堆栈。
下面以数组实现为例来介绍如何创建堆栈。
首先,我们需要定义一个固定大小的数组,用来存储堆栈中的元素。
通常,我们还需要定义一个指针变量top,用来指示当前堆栈顶部元素的位置。
初始时,top 的值为-1,表示堆栈为空。
2. 入栈操作当需要向堆栈中插入一个元素时,我们先将top 的值加一,然后将元素放入数组中的对应位置即可。
以下是入栈的基本操作流程:判断堆栈是否已满(如果是数组实现的堆栈);如果堆栈未满,则将要插入的元素放入top+1 的位置;更新top 的值,指向新的堆栈顶部元素;3. 出栈操作当需要从堆栈中删除一个元素时,我们首先获取top 处的元素,然后将top 的值减一即可。
以下是出栈的基本操作流程:判断堆栈是否为空;如果堆栈非空,则将top 处的元素取出;更新top 的值,指向新的堆栈顶部元素;4. 获取栈顶元素除了入栈和出栈操作,获取栈顶元素也是堆栈的常用操作。
我们可以通过top 指针来获取堆栈顶部的元素,而不对堆栈做任何改动。
5. 示例下面我们通过一个简单的示例来展示堆栈的基本操作。
假设我们有一个大小为5 的数组来实现堆栈,初始时堆栈为空。
首先,我们将top 的值初始化为-1。
然后我们按照如下流程进行操作:入栈操作:依次将元素A、B、C 入栈;出栈操作:依次将元素C、B 出栈;获取栈顶元素:此时栈顶元素为A;通过上述示例,我们可以清晰地了解堆栈的基本操作方法。
arm堆栈操作arm堆栈的组织结构是满栈降的形式,满栈即sp是要停留在最后一个进栈元素,降:就是堆栈的增长方向是从高地址向低地址发展。
arm对于堆栈的操作一般采用LDMFD (pop)和STMFD (push) 两个命令。
以前困惑的就是STMFD 命令对于操作数是按照什么顺序压栈的比如:STMFD sp!{R0-R5,LR} 进栈顺序是:高地址(1方式)LRR5R4```````R0 <-sp低地址还是:高地址(2方式)R0R1```R5LR <-sp低地址现在通过下表,可以轻松的解决这个问题:按照图表,可知STMFD对应的是STMDB,根据arm指令手册,可知STMDB入栈顺序是(1方式)而LDMFD对应的是LDMIA,这样这两个操作就可以成功配对:以下是我在学习ARM指令中记录的关于堆栈方面的知识1、寄存器R13 在ARM 指令中常用作堆栈指针2、对于R13 寄存器来说,它对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:R13_<mode> 其中,mode为以下几种模式之一:usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。
而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
4、有四种类型的堆栈:堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(DecendingStack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。
这样就有四种类型的堆栈工作方式,ARM 微处理器支持这四种类型的堆栈工作方式,即:◎Full descending 满递减堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用Full descending 类型堆栈。
<这是什么原因呢?>◎Full ascending 满递增堆栈堆栈首部是低地址,堆栈向高地址增长。
栈指针总是指向堆栈最后一个元素(最后一个元素是最后压入的数据)。
◎Empty descending 空递减堆栈堆栈首部是低(这里是不是错了,应该是高地址吧)地址,堆栈向高地址增长。
栈指针总是指向下一个将要放入数据的空位置。
◎Empty ascending 空递增堆栈堆栈首部是高地址,堆栈向低地址增长。
栈指针总是指向下一个将要放入数据的空位置。
5、操作堆栈的汇编指令堆栈类型入栈指令出栈指令Full descending STMFD (STMDB) LDMFD (LDMIA) Full ascending STMFA (STMIB) LDMFA (LDMDA) Empty descending STMED (STMDA) LDMED (LDMIB) Empty ascending STMEA (STMIA) LDMEA (LDMDB)例子:STMFD r13!, {r0-r5} ; Push onto a Full Descending Stack LDMFD r13!, {r0-r5} ; Pop from a Full Descending Stack.例子1)保护现场参数,不影响PC,嵌汇编的时候对之前的存参数的寄存器R0~R12保存STMFD r13!, {r0-r7,LR}LDMFD r13!, {r0-r7,PC}2) ARM汇编中lr(r14)寄存器的作用lr(r14)的作用问题,这个lr一般来说有两个作用:1.当使用bl或者blx跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复。
2.异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址。
另外注意pc,在调试的时候显示的是当前指令地址,而用mov lr,pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用mov pc,pc,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果.3.我们看到的LR值是上一个子程序调用保存的子程序返回地址,这个LR是要赋给PC 的。
嵌入式汇编要手动保存返回地址,进行现场保护。
PC记录当前运行的地址。
下一条回自己+4进入子程序,LR才自动更新为返回地址值,PC为程序运行地址ARM汇编嵌套子程序几个星期前阅读了(加)Carl Hamacher、Zvonko Vranesic、Safwat Zaky编写的《计算机组成》第五版中的ARM子程序调用的一些知识,启发很大,顺便将它整理了一下并加入了自己的理解。
子程序1 通过寄存器传递参数BL指令通常用于调用一个子程序。
它和B 指令的区别在于它将返回地址装载到R14中。
由于子程序可能是嵌套的,因此LR的内容必须保存在子程序所使用的堆栈中。
下面的例子使用寄存器传递参数。
调用者通过寄存器R1和R2分别将数组的大小和数组的首地址传递给子程序;子程序利用寄存器R0将和传递给调用者。
该子程序使用了寄存器R3,必须将它和LR推入堆栈。
调用程序LDR R1, NLDR R2, POINTERBL LISTADDSTR R0, SUM…子程序LISTADD STMFD R13!, {R3, R14}MOV R0, #0LOOP LDR R3, [R2], #4ADD R0, R0, R3SUBS R1, R1, #1BGT LOOPLDMFD R13!, {R3, R15}注:这里并没有遵守APCS(ARM过程调用标准),一般由调用者负责保存R0~R3,被调用者负责保存其他的寄存器以使调用返回后程序的状态不被破坏。
2 通过堆栈传递参数调用程序LDR R0, POINTERSTR R0, [R13, #-4]! ;将数组首地址推入堆栈LDR R0, NSTR R0, [R13, #-4]! ;将元素个数N推入堆栈BL LISTADDLDR R0, [R13, #4] ;将元素和装载到寄存器R0中STR R0, SUMADD R13, R13, #8 ;恢复堆栈…子程序LISTADD STMFD R13!, {R0-R3, R14} LDR R1, [R13, #20]LDR R2, [R13, #24]MOV R0, #0LOOP LDR R3, [R2], #4ADD R0, R0, R3SUBS R1, R1, #1BGT LOOPSTR R0, [R13,#24] ;把和推入堆栈的最深处LDMFD R13!, {R0-R3, R15}3 嵌套子程序当子程序嵌套时,堆栈是用于处理返回地址的最合适的数据结构。
当调用子程序时在堆栈上建立了完整的堆栈结构。
应当注意当前子程序的堆栈帧指针所指向的空间中存储的是调用当前子程序的子程序的堆栈帧指针。
调用者将子程序所需要的参数按照顺序推入堆栈。
子程序首先保存工作寄存器、调用者的堆栈帧指针以及返回地址,然后它计算自己的堆栈帧指针的值(ADD FP, SP, #16),并利用这个堆栈帧指针从堆栈帧中获取调用者传递给它的参数。
在子程序完成它的任务之后,它也将返回值保存在堆栈中,此例保存在参数所在的内存单元。
调用者和被调用者必须约定好参数的传递顺序和返回值保存位置。
如果返回值比较多的话,调用者要为返回值预先在堆栈中保留合适的空间。
调用程序2000 LDR R0, PARAM2STR R0, [SP, #-4]! ;将参数推入堆栈LDR R0, PARAM1STR R0, [SP, #-4]!BL SUB12020 LDR R0, [SP] ;保存SUB1的结果STR R0, RESULTADD SP, SP, #8 ;恢复堆栈…子程序2100 SUB1 STMFD SP!, {R0-R3, FP,LR}ADD FP, SP, #16 ;计算帧指针LDR R0, [FP, #8] ;载入参数1LDR R1, [FP, #12] ;载入参数2…LDR R2, PARAM3 ;载入参数3STR R2, [SP, #-4]! ;将参数3推入堆栈BL SUB22164 LDR R2, [SP], #4 ;将SUB2的结果弹出并存储在R2中,并递增SP…STR R3, [FP, #8] ;将结果推入堆栈LDMFD SP!, {R0-R3, FP, PC} ;恢复寄存器并返回3000 SUB2 STMFD SP!, {R0, R1, FP, LR}ADD FP, SP, #8 ;载入结构指针LDR R0, [FP, #8] ;载入参数…STR R1, [FP, #8] ;将结果推入堆栈LDMFD SP!, {R0, R1, FP, PC}3 子程序编译后都放在哪嵌汇编子程序:会在编译后统一放到一个地方系统调用__main()库函数,后再进入main.c前的初始化C下面的堆栈命令前(这个堆栈是用来放置C里参数的)嵌汇编子程序:按定义顺序放在堆栈前,而C语言子程序,放在初始化堆栈堆栈命令后。