为什么我们不能直接将数据从一个内存位置移动到另一个内存位置。
如果我问的是一个愚蠢的问题,请原谅我,但我认为这是一个真实的情况,至少对于我遇到的情况(8085,8086 n 80386)
我并不是真的在寻找移动数据的解决方案(例如,使用 movs n all),但实际上是这种异常的原因。
为什么我们不能直接将数据从一个内存位置移动到另一个内存位置。
如果我问的是一个愚蠢的问题,请原谅我,但我认为这是一个真实的情况,至少对于我遇到的情况(8085,8086 n 80386)
我并不是真的在寻找移动数据的解决方案(例如,使用 movs n all),但实际上是这种异常的原因。
MOVS呢?它将 esi 寻址的 8/16/32 位值移动到 edi 寻址的位置。
基本原因是大多数指令集允许一个寄存器操作数和一个内存操作数,坚持这种格式使得指令解码器的设计更容易。它还使 CPU 内部的执行引擎更容易,因为该指令通常可以对一个内存位置发出内存操作,并且最多可以读取或写入一个寄存器块。
直接执行内存到内存指令需要指定两个内存位置。考虑到寄存器/内存指令格式,这很尴尬。鉴于机器的性能,几乎没有理由为此修改指令格式。
更现代的 CPU 使用的一种技巧是提供某种类型的块移动指令,其中源和目标位置位于寄存器中(对于 X86,这分别是 ESI 和 EDI)。然后一条指令可以只指定两个寄存器(或者在 x86 的情况下,指令只知道哪些寄存器)。这解决了指令解码问题。
指令执行问题有点困难,但人们有很多晶体管。组织从一个寄存器间接读取,通过另一个寄存器间接写入,并在硅中增加两者都很尴尬,但这只会消耗一些晶体管。现在,正如您所要求的那样,您可以拥有从内存移动到内存的指令。为 X86 标注的其他海报之一有一些指令(MOVB、MOVW、MOVS,...),一次一个内存字节/字/...。
移动一块内存将是理想的,因为 CPU 可以生成高带宽读取和写入。x86 使用 MOV- 上的 REP(重复)前缀来移动更大的块。
但是如果一个指令可以做到这一点,你就会遇到一个问题,那就是它可能需要很长时间才能执行(移动 1Gb 需要多长时间?--> 数百万个时钟周期!),这会破坏 CPU 的中断响应率。
x86 通过允许 REP MOV- 被中断来解决这个问题,同时将 PC 设置回指令的开头。通过在移动过程中适当地更新寄存器,您可以中断并重新启动 REP MOV 指令,同时具有快速块移动和高中断响应率。管子下有更多的晶体管。
RISC 人员发现块移动指令的所有这些复杂性大多不值得。您可以编写一个哑循环(甚至是 x86):
copy: MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
DEC ECX
JNE copy
它与 REP MOV- 做同样的基本事情。几乎现代 CPU(x86 等)执行速度如此之快(超标量等),以至于总线与自定义移动指令一样被使用,但现在您不需要所有那些浪费的晶体管(或相应的热量)。
大多数 CPU 品种不允许内存到内存的移动。通常 CPU 一次只能访问一个内存位置,这意味着在移动它时需要一个临时位置来存储值(通常是通用寄存器)。如果您考虑一下,直接从一个内存位置移动到另一个位置将需要 CPU 能够同时访问 RAM 中的两个不同位置——这意味着至少有两个完整的内存控制器,即使那样,它们“播放”的机会好”足以访问相同的 RAM 将非常糟糕。芯片设计人员可能已经能够使用一些技巧来允许从一个 RAM 芯片直接复制到另一个,但这将是一种非常特殊的应用功能,只会增加成本和复杂性来解决一个非常罕见的问题。
您也许可以使用一些特殊的 DMA 硬件,使其看起来像在没有临时存储的情况下移动内存一样,至少从 CPU 的角度来看是这样。
在 CPU 和 RAM 之间有一组地址线、一组数据线和几条控制线。如果没有第二组地址线和 RAM 内的一大堆复杂逻辑,您就无法直接从内存移动到内存。因此,我们必须将其临时存储在寄存器中。
您可以编写一条同时执行加载和存储的指令,并且在程序员看来就像一条指令,但是还有其他考虑因素,例如指令大小、有效地址计算逻辑的不重复、流水线等,因此需要保留它更简单。
内存-内存机器通常比加载存储机器慢。这是 RISC 研究人员在 1980 年左右推断/计算/发明的。因此,较旧的架构(VAX/OS360)往往具有内存-内存架构;较新的机器进行加载存储。
另一个有趣的变体是堆栈机器。他们似乎总是作为少数人出现。