Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arm 堆栈指针和程序计数器之间有什么区别?_Arm_Microprocessors_Program Counter_Stack Pointer - Fatal编程技术网

Arm 堆栈指针和程序计数器之间有什么区别?

Arm 堆栈指针和程序计数器之间有什么区别?,arm,microprocessors,program-counter,stack-pointer,Arm,Microprocessors,Program Counter,Stack Pointer,众所周知,微处理器执行任务的过程就是从内存中逐个执行二进制指令,并且有一个程序计数器保存下一条指令的地址。如果我没有错的话,这就是处理器执行任务的方式。但也有另一个指针名为Stack pointer,它的作用与程序计数器几乎相同。我的问题是为什么我们需要一个指向内存点地址(堆栈)的堆栈指针?有人能告诉我堆栈指针和程序计数器之间的主要区别吗?嗯,它们是根本不同的概念。它们都包含内存地址,但请记住,指令和数据(实际上)都保存在相同的内存空间中 程序计数器包含当前正在执行的指令的地址。事实上,CPU使

众所周知,微处理器执行任务的过程就是从内存中逐个执行二进制指令,并且有一个程序计数器保存下一条指令的地址。如果我没有错的话,这就是处理器执行任务的方式。但也有另一个指针名为Stack pointer,它的作用与程序计数器几乎相同。我的问题是为什么我们需要一个指向内存点地址(堆栈)的堆栈指针?有人能告诉我堆栈指针和程序计数器之间的主要区别吗?

嗯,它们是根本不同的概念。它们都包含内存地址,但请记住,指令和数据(实际上)都保存在相同的内存空间中

程序计数器包含当前正在执行的指令的地址。事实上,CPU使用程序计数器中的值在执行指令之前获取指令。在执行指令时,其值将递增,如果代码分支,其值将被强制覆盖

堆栈指针包含堆栈顶部的地址,该地址是运行代码用作草稿行的内存区域。值临时存储在那里,函数的参数有时放在那里,代码地址也可以存储在那里(例如,当一个函数调用另一个函数时)。

void show(unsigned int);
无符号整数(无符号整数x)
{
如果(x&1)显示(x+1);
返回(x | 1);
}
0000200c:
200c:e3100001 tst r0,#1
2010:e92d4010推送{r4,lr}
2014年:e1a04000 mov r4,r0
2018年:1a000002 bne 2028
201c:e3840001 orr r0,r4,#1
2020年:e8bd4010 pop{r4,lr}
2024年:E12FF1E bx lr
2028年:E280001加上r0,r0,#1
202c:EBFFF5 bl 2008
2030年:e3840001 orr r0,r4,#1
2034:e8bd4010 pop{r4,lr}
2038:E12FF1E bx lr
使用一个简单的函数,在这个问题上标记arm时,使用其中一个arm指令集编译和反汇编

让我们假设一个简单的串行非管道旧式执行

为了到达这里,发生了一个调用(此指令集中的bl、分支和链接),该调用将程序计数器修改为0x200C。程序计数器用于获取该指令0xe3100001,然后在执行前获取后,程序计数器设置为指向下一条指令0x2010。由于此程序计数器是针对此特定指令集描述的,因此它获取并暂存下一条指令0xe92d4010,并且在执行0x200C指令之前,pc包含值0x2014,前面有两条指令。出于演示目的,让我们设想一下我们从0x200C获取了0xe3100001。pc现在设置为0x2010,等待执行完成并等待下一个获取周期

第一条指令测试r0的lsbit,即传入的参数(x),程序计数器未修改,因此下一次提取从0x2010读取0xE92D4100

程序计数器现在包含0x2014,执行0x2010指令。此指令是一个push指令,它使用堆栈指针。作为程序员,在进入这个函数时,我们不关心堆栈指针的确切值是多少,它可能是0x2468,也可能是0x4010,我们不关心。因此,我们只说它包含sp_start的值/地址。这个push指令使用堆栈来保存两件事,一件是链接寄存器lr,r14,返回地址,当这个函数完成时,我们要返回到调用函数。r4,根据编译器对该指令集使用的调用约定的规则,r4必须保留,如果修改它,必须将其返回到调用时的值。因此,我们将把它保存在堆栈上,而不是把x放在堆栈上,在这个函数中多次引用x,这个编译器选择保存r4中的任何内容(我们不在乎,我们只需要保存它),并使用r4在这个函数编译期间保存x。我们调用和他们调用的任何函数都会保留r4,所以当我们调用的任何函数返回给我们时,r4就是我们调用时的任何函数。因此,堆栈指针本身更改为sp_start-8,sp_start-8保存r4的副本,sp_start-4保存lr或r14的副本,我们现在可以修改r4或lr,因为我们希望有一个带有保存副本的便笺簿(堆栈)和一个指针,我们可以对其进行相对寻址以获取这些值,任何想要使用堆栈的调用函数都将从sp_start-8开始向下扩展,而不是踩在我们的便笺簿上

现在我们获取0x2014,将pc更改为0x2018,这将在r4中生成x的副本(在r0中传递),以便稍后在函数中使用它

我们获取0x2018,将pc更改为0x201C。这是一个条件分支,因此根据条件,pc将保持0x201C或更改为0x2028。所讨论的标志是在执行tst r0时设置的,#1其他指令没有触及该标志。所以我们现在有两条路要走,如果条件不成立,那么我们使用0x201C来获取

从0x201c获取将pc更改为0x2020,执行x=x | 1,r0是包含函数返回值的寄存器。此指令不修改程序计数器

从0x2020获取将pc更改为0x2024,执行pop。我们没有修改堆栈指针(另一个保留的寄存器,您必须将其放回找到它的位置),因此sp等于sp_start-8(即sp+0)。现在,我们从sp_start-8读取该值,并将其放入r4,从sp_start-4(即sp+4)读取该值,然后将该值放入lr,并将8添加到堆栈指针,以便现在将其设置为sp_start,当我们开始的时候它的价值,把它放回你找到它的方式

从0x2024获取将pc更改为0x2028。bx lr是r14的一个分支,基本上是函数的返回,
void show ( unsigned int );
unsigned int fun ( unsigned int x )
{
    if(x&1) show(x+1);
    return(x|1);
}

0000200c <fun>:
    200c:   e3100001    tst r0, #1
    2010:   e92d4010    push    {r4, lr}
    2014:   e1a04000    mov r4, r0
    2018:   1a000002    bne 2028 <fun+0x1c>
    201c:   e3840001    orr r0, r4, #1
    2020:   e8bd4010    pop {r4, lr}
    2024:   e12fff1e    bx  lr
    2028:   e2800001    add r0, r0, #1
    202c:   ebfffff5    bl  2008 <show>
    2030:   e3840001    orr r0, r4, #1
    2034:   e8bd4010    pop {r4, lr}
    2038:   e12fff1e    bx  lr