Zilog Z80操作码实现

15

我目前正在开发一款好玩的GameBoy模拟器,但是我在理解一些基本操作码的实现方式时遇到了一些问题。

目前我正在实现AND操作;前几个(0xA0-> 0xA3; 0xA6&0xA7)都很简单,但对于寄存器H,L的AND操作有点不同。

您可以通过以下链接下载z80文档: um0080.pdf(第172页)

这里有一些示例以说明我的意思(使用伪代码),基本上我所做的就是:

AND A,H(请注意位移)

(read HL register; >> 8) save in cache C
R->C = R->HL >> 8;

perform AND operation with cache
AND_H(R->C);
R->A &= R->C;

AND A,L (note the bit-masking)

AND A,L(请注意位屏蔽)

(read HL register; &0xFF) save in cache C
R->C = R->HL &0xFF;
我了解所有的位运算,知道它们的作用,但似乎我无法弄清楚为什么需要这样做。我有一些理论(如果我错了,请纠正我 :-)):
我已经了解到,寄存器H和L基本上是寄存器HL,它是一个16位寄存器。由于CPU/总线只能处理8位操作,因此需要分割它;或者更合适的建议是:由于它只是一个寄存器,H和L的值在寄存器中被掩码,它们只需要被分开(较高的/低的四位?)。
如果有人可以让我更清楚地了解这一点,我将非常感激,因为我只想拥有更多的背景知识(关于所有这些东西内部如何工作),所以我知道我在做什么非常重要。

1
不需要那样做。显然,如果你要把HL放在一起,当你使用其中的一半时,你必须将其“分开”。另一个选择是将H和L分开,并在需要HL时将它们组合在一起。 - harold
我觉得你对事情的理解相当不错。 - Michael Burr
你有没有检查过现有的Z80模拟器源代码? - David Pointer
@harold:是的,我已经想到了,它只取决于你如何实现它;因为它全部都是虚拟的,你可以随心所欲地做任何事情,只要输出(类似黑盒)是合法的即可。 - Sepultura
@michael:呵呵,谢谢;这给了我一些希望 - 仍在努力学习。 - Sepultura
1
顺便说一下,Zilog Z80手册是一份不错的文档,去年夏天我在调试ADA MP-1吉他前置放大器(1987年版)时使用它作为参考。 :) - Kaz
1个回答

11
正如评论所指出的那样,你找到的实现将H和L寄存器作为16位实体HL一起存储,然后通过向右移位并屏蔽来将其分解为H和L,这纯粹是一种特定于实现的方法。
原始的z80有一个4位ALU(请参见this transcript of a Computer History Museum panel第9页底部开始的Shima的评论),因此它实际上会(i)与L的低四位和累加器进行AND运算;然后(ii)与L的高四位和累加器进行AND运算。但是,它将其寄存器公开为离散的8位实体,因此内部实现完全隐藏。
HL被称为寄存器对,因为它是两个寄存器一起组成的16位数量。在忽略阴影和索引寄存器的情况下,原始的z80实际上有三个这样的寄存器对——HL、BC和DE。BC和DE在Gameboy的CPU中作为间接加载的备用对(例如,操作码0x1a——LD A,(BC))和16位算术的备用对(例如,0x09 ADD HL,BC),并在z80上有其他一些用途。

SP和PC通常被视为不可分割的16位寄存器(当然,你可以通过将它们存储到内存中并逐个读取字节来分割它们),AF用于推入和弹出,但F是一个特殊情况,因此AF作为16位整数通常不是特别有用。

简而言之,你不需要理解操作码如何被实现,只需要了解它们是如何被特定作者实现的。


2
事实证明,IX和IY寄存器也可以使用未记录的操作码进行单独的字节寻址。将HL转换为IX / IY的操作码前缀为0xBD和0xFD。实质上,使用0xBD作为其他基于寄存器的操作的前缀,将(HL)的引用转换为(IX + n)(其中n是单字节整数),但它还将对H的引用转换为IX的高半部分,并将对L的引用转换为IX的低半部分。同样适用于IY和操作码前缀FD。 - torek
感谢您添加对4位ALU的引用!+1 - guga
哇,非常感谢Tommy!现在我知道的比我预期的还要多 :-); 还有感谢你提供的链接,这是一份非常有趣的文档 - 完全值得阅读。 - Sepultura

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接