Ben*_*tto 87 architecture stack history
我知道在我个人熟悉的架构(x86,6502等)中,堆栈通常会向下增长(即,每个推入堆栈的项目都会导致SP递减,而不是递增的SP).
我想知道这个的历史原因.我知道在一个统一的地址空间中,在数据段的另一端(例如)开始堆栈很方便,所以如果双方在中间发生碰撞,那么只会出现问题.但是为什么堆栈传统上是最重要的?特别是考虑到这与"概念"模型的对立面如何?
(请注意,在6502架构中,堆栈也向下增长,即使它被限制在一个256字节的页面上,这个方向选择似乎是任意的.)
pax*_*blo 47
至于历史原因,我不能肯定地说(因为我没有设计它们).我对此事的想法是,早期的CPU将其原始程序计数器设置为0,并且自然希望在另一端启动堆栈并向下增长,因为它们的代码自然会向上增长.
另外,请注意,所有早期CPU 的程序计数器设置为0时的设置不是这种情况.例如,摩托罗拉6809将从地址获取程序计数器,
0xfffe/f因此您可以开始在任意位置运行,具体取决于该地址提供的内容(通常,但不限于ROM).
一些历史系统首先要做的事情之一就是从顶部扫描内存,直到找到一个可以读回写入的相同值的位置,这样就可以知道安装的实际RAM(例如,带有64K地址空间的z80)不一定有64K或RAM,事实上64K 在我的早期会是巨大的).一旦找到顶部实际地址,它将适当地设置堆栈指针,然后可以开始调用子例程.这种扫描通常由CPU在ROM中运行代码来完成,作为启动的一部分.
关于堆栈增长,并非所有堆栈都向下增长,请参阅此答案以获取详细信息.
roo*_*ebo 10
Stanley Mazor(4004 和 8080 架构师)在“英特尔微处理器:8008 到 8086”中解释了如何为 8080(最终为 8086)选择堆栈增长方向:
堆栈指针被选择为“下坡”运行(堆栈向较低的内存前进)以简化从用户程序(正索引)到堆栈的索引并简化从前面板显示堆栈的内容。
一个可能的原因可能是它简化了对齐.如果在堆栈上放置一个必须放在4字节边界上的局部变量,则可以简单地从堆栈指针中减去对象的大小,然后将两个较低位置零以获得正确对齐的地址.如果堆栈向上增长,确保对齐变得有点棘手.
POP使用与通常用于扫描字符串和数组相同的寻址模式从堆栈中弹出值的指令需要执行两件事:从内存中读取值并调整堆栈指针。此操作有四种可能的设计选择:
先预增堆栈指针,然后读取值。这意味着堆栈将“向下”增长(朝向较低的内存地址)。
先预减堆栈指针,然后读取值。这意味着堆栈将“向上”增长(朝向更高的内存地址)。
先读取值,然后后递增堆栈指针。这意味着堆栈将向下增长。
首先读取值,然后对堆栈指针进行后递减。这意味着堆栈将向上增长。
在许多计算机语言(特别是 C)中,字符串和数组作为指向其第一个元素的指针传递给函数。一个非常常见的操作是从第一个元素开始按顺序读取字符串或数组的元素。这样的操作只需要上述的后递增寻址方式。
此外,读取字符串或数组的元素比写入元素更常见。事实上,有许多标准库函数根本不执行任何写入操作(例如,,,strlen())!strchr()strcmp()
因此,如果指令集设计中的寻址模式数量有限,则最有用的寻址模式将是后增量读取。这不仅产生了最有用的字符串和数组操作,而且产生了使堆栈向下增长的POP指令。
第二个最有用的寻址模式是后递减写入,可用于匹配PUSH指令。
事实上,PDP-11 具有后递增和前递减寻址模式,这产生了向下增长的堆栈。甚至VAX也没有预增量或后减量。