将寄存器加载到自身的指令的目的是什么?

What's the purpose of instructions for loading a register to itself?

在查看 Gameboy 的指令集时,我遇到了如下指令:

LD A, A
LD B, B
LD C, C
LD D, D

...

这些指令中的每一个在 this table 中都有自己的操作码,这让我认为它们具有一定的重要性,因为可能的操作码数量受到限制。

我首先想到它可能会取消引用该寄存器中的指针并将值存储在该指针处(like in this question), but in an emulatorLD A, A 实现为:

Z80._r.a = Z80._r.a

它们似乎对处理器的状态没有影响(只是将寄存器设置为它们自己的值)并且执行的周期数与 NOP 相同。

为什么指令集中包含这些操作码,它们的作用是什么?

他们简化了解码单元,如果你会检查

7F LD A,A
78 LD A,B
79 LD A,C

47 LD B,A
40 LD B,B
41 LD B,C

4F LD C,A
48 LD C,B
49 LD C,C

你可以注意到,底部的 3 位是为源寄存器保留的(值 0-7 B,C,D,E,H,L,(HL),A),它们旁边的 3 位是目标寄存器,同样具有相同的 0-7 含义(因此 0 vs 0 创建 LD B,B),而前两位 01 select LD,乍一看我不确定我是否完美地破译了它。

人们还希望 76 成为 LD (HL),(HL),这比 LD A,A 更没有意义,所以有特殊的逻辑来捕捉那个并做 HALT相反。

所以这是关于指令解码器的简单性,使用与 select source/target 寄存器相同的位模式,并且不添加更多晶体管来捕捉 same,same 情况,除了 (HL),(HL)(这可能会在需要内存访问的源和目标上发生内部故障,因此额外的 "logic" 在硬件设计中可能相当简单。

请记住,早期的 CPU 通常是手工设计的,晶体管的总数量必须保持在较低水平以适合芯片,并且易于管理以手工绘制电路并验证其正确性。

编辑:Z80 有大约 8500 个晶体管,您可能需要检查:https://en.wikipedia.org/wiki/Transistor_count and https://en.wikipedia.org/wiki/Zilog_Z80 ... GameBoy 对 Z80 进行了一些修改,但晶体管总数将非常接近原始价值,虽然我没有搜索确切的价值,而且我不确定任天堂将它扩展到多远的未来,也许他们已经负担得起 20-50k 的东西,但我对此表示怀疑。


附录:最近我读到有关俄罗斯 Sinclair ZX Spectrum 克隆的消息,这些机器经过大量修改,增加了额外的功率、内存和功能......其中一些正在使用这些 ld same,same 操作码来控制DMA 传输,因此在这些机器上使用它们作为 nop 的代码可能无法正确执行。这与 GameBoy 无关,但如果你有二进制目标 "Sprinter" 或类似的俄罗斯 ZX 克隆之一,并且你在反汇编中找到其中之一,请不要自动考虑它们 nop,它们可能成为实际执行某些操作的有效代码的一部分(最有可能使用 DMA)。

这些奇怪的 NOP 指令一直追溯到英特尔 8008 的原始祖先处理器。在该芯片中,它们仅仅是执行寄存器移动指令的结果。允许 MOV A,A 等简化了指令解码器并节省了硅片 space.

从 8080 到 Z80(及更高版本),这些成为保持向后兼容性所必需的。他们甚至以

的形式生存到 x86 世界

MOV AL,AL 等

所以大多数现代台式机仍然支持这些奇怪的指令。

注意:我在描述英特尔机器时使用了英特尔助记符。请放心,这些 assemble 与 Zilog 助记符相同的二进制代码。