"self-modified link" 在 Pegasus 编程中如何工作?
How does "self-modified link" work in Pegasus programming?
我一直在研究 1950 年代 'Pegasus' 计算机的模拟,并遇到了术语 "self-modified link"。这是如何工作的?
首先要注意的是:
- 代码块被加载到顺序寄存器中以供执行
- 每个寄存器包含 39 位:一个数据字或两个指令。
Pegasus 计算机通过传递两条指令(在一个寄存器中)来支持 subroutine/function 调用,说明被调用者应该如何 return — 作为数据,即作为参数。然后,被调用者将获取该参数,将其存储到自己的本地内存中,并将控制权转移给它——从而执行由调用者作为数据传递的两条指令。
来电者会这样做:
load instructions from L1: into X1 register
... pass other parameters ...
load callee's first block into U0
transfer control to U0 at start
L1: load instructions from main block back into U0
transfer control to U0 at desired caller resume point (e.g. L2)
L2: ...caller resumes...
调用者会这样做:
... compute something ...
store X1 into L4 # writes the two instruction pair in X1 into L4
transfer control to L4 # may or may not be present depending on location of L4
L4: # starts empty but contains the two instructions that came from L1
# so control is transferred back to the caller
L1 的序列从不直接执行,而是作为参数传递给被调用方的指令模板,被调用方会将模板存储到自己的本地内存中,然后执行副本。
他们使用术语 "cue" 来表示调用(子例程调用,例如将控制权转移给被调用者),并使用 "link" 来表示 return(将控制权从子例程转移回正确的调用者)(以及机器代码指令的术语 "orders")。
所以,当他们谈到 "self-modfying link" 时,他们的意思是子例程 return 到其调用者的机制使用 self-modfying code,这是改变其自身指令的代码( s) 执行时,即写入指令存储器以供即将执行的代码。这里是将X1(L1处代码的副本)写入L4,写入后立即执行。
如今,自修改代码通常不受欢迎,因为它有几个负面影响:指令缓存需要与自修改保持同步,这会影响性能,并且,指令内存必须是可写的,这会影响安全性。
由于我们今天可能认为指令集中缺少指令,旧处理器中经常使用自修改——例如 PDP-8 有 I/O 指令,但I/O 端口被硬编码到指令中——没有间接的 I/O 端口访问。因此,为了让子程序访问作为参数的 I/O 端口,使用自修改代码构造到该端口的 I/O 指令。 (也可以使用大型 switch 语句,但会占用更多代码。)这些较旧的处理器没有指令缓存,程序员和开发工具也没有强制将指令部分与数据部分分开(即它们没有试图保护指令)。
在 Pegasus 的情况下,没有间接分支,阻碍了 return 到调用者提供的地址的能力。事实证明,调用者的代码可能实际上也被覆盖了——传递两条指令让被调用者执行的能力允许为调用者的代码恢复一个寄存器块并在该块内跳转。
(并且将完全形成的指令作为参数传递导致 return 链接的指令少于其他自修改替代方案:动态构造给定地址参数的跳转指令 - 这将花费更多进行位域操作的说明。)
https://en.wikipedia.org/wiki/Ferranti_Pegasus
http://bitsavers.org/pdf/ferranti/pegasus/PegasusProgrammingMan_1962.pdf
摘自 George Felton 的 Pegasus 编程手册第 119 页。
这看起来像是真正的答案。
我一直在研究 1950 年代 'Pegasus' 计算机的模拟,并遇到了术语 "self-modified link"。这是如何工作的?
首先要注意的是:
- 代码块被加载到顺序寄存器中以供执行
- 每个寄存器包含 39 位:一个数据字或两个指令。
Pegasus 计算机通过传递两条指令(在一个寄存器中)来支持 subroutine/function 调用,说明被调用者应该如何 return — 作为数据,即作为参数。然后,被调用者将获取该参数,将其存储到自己的本地内存中,并将控制权转移给它——从而执行由调用者作为数据传递的两条指令。
来电者会这样做:
load instructions from L1: into X1 register
... pass other parameters ...
load callee's first block into U0
transfer control to U0 at start
L1: load instructions from main block back into U0
transfer control to U0 at desired caller resume point (e.g. L2)
L2: ...caller resumes...
调用者会这样做:
... compute something ...
store X1 into L4 # writes the two instruction pair in X1 into L4
transfer control to L4 # may or may not be present depending on location of L4
L4: # starts empty but contains the two instructions that came from L1
# so control is transferred back to the caller
L1 的序列从不直接执行,而是作为参数传递给被调用方的指令模板,被调用方会将模板存储到自己的本地内存中,然后执行副本。
他们使用术语 "cue" 来表示调用(子例程调用,例如将控制权转移给被调用者),并使用 "link" 来表示 return(将控制权从子例程转移回正确的调用者)(以及机器代码指令的术语 "orders")。
所以,当他们谈到 "self-modfying link" 时,他们的意思是子例程 return 到其调用者的机制使用 self-modfying code,这是改变其自身指令的代码( s) 执行时,即写入指令存储器以供即将执行的代码。这里是将X1(L1处代码的副本)写入L4,写入后立即执行。
如今,自修改代码通常不受欢迎,因为它有几个负面影响:指令缓存需要与自修改保持同步,这会影响性能,并且,指令内存必须是可写的,这会影响安全性。
由于我们今天可能认为指令集中缺少指令,旧处理器中经常使用自修改——例如 PDP-8 有 I/O 指令,但I/O 端口被硬编码到指令中——没有间接的 I/O 端口访问。因此,为了让子程序访问作为参数的 I/O 端口,使用自修改代码构造到该端口的 I/O 指令。 (也可以使用大型 switch 语句,但会占用更多代码。)这些较旧的处理器没有指令缓存,程序员和开发工具也没有强制将指令部分与数据部分分开(即它们没有试图保护指令)。
在 Pegasus 的情况下,没有间接分支,阻碍了 return 到调用者提供的地址的能力。事实证明,调用者的代码可能实际上也被覆盖了——传递两条指令让被调用者执行的能力允许为调用者的代码恢复一个寄存器块并在该块内跳转。
(并且将完全形成的指令作为参数传递导致 return 链接的指令少于其他自修改替代方案:动态构造给定地址参数的跳转指令 - 这将花费更多进行位域操作的说明。)
https://en.wikipedia.org/wiki/Ferranti_Pegasus http://bitsavers.org/pdf/ferranti/pegasus/PegasusProgrammingMan_1962.pdf
摘自 George Felton 的 Pegasus 编程手册第 119 页。
这看起来像是真正的答案。