为这个看似次要的问题道歉,但我似乎无法在任何地方找到答案 - 我只是想在我的 Z80 模拟器中实现 DAA 指令,我在 Zilog 手册中注意到它是为了调整二进制编码十进制算术的累加器。它说该指令旨在在加法或减法指令之后立即运行。
我的问题是:
- 如果它在另一条指令之后运行会发生什么?
- 它怎么知道它之前的指令?
- 我意识到有 N 标志 - 但这肯定不会明确表明前一条指令是加法或减法指令?
- 不管前面的指令如何,它是否只是根据 DAA 表中规定的条件修改累加器?
不管前面的指令如何,它是否只是根据 DAA 表中规定的条件修改累加器?
是的。该文档仅告诉您 DAA 的用途。也许您指的是此链接中的表格:
--------------------------------------------------------------------------------
| | C Flag | HEX value in | H Flag | HEX value in | Number | C flag|
| Operation | Before | upper digit | Before | lower digit | added | After |
| | DAA | (bit 7-4) | DAA | (bit 3-0) | to byte | DAA |
|------------------------------------------------------------------------------|
| | 0 | 0-9 | 0 | 0-9 | 00 | 0 |
| ADD | 0 | 0-8 | 0 | A-F | 06 | 0 |
| | 0 | 0-9 | 1 | 0-3 | 06 | 0 |
| ADC | 0 | A-F | 0 | 0-9 | 60 | 1 |
| | 0 | 9-F | 0 | A-F | 66 | 1 |
| INC | 0 | A-F | 1 | 0-3 | 66 | 1 |
| | 1 | 0-2 | 0 | 0-9 | 60 | 1 |
| | 1 | 0-2 | 0 | A-F | 66 | 1 |
| | 1 | 0-3 | 1 | 0-3 | 66 | 1 |
|------------------------------------------------------------------------------|
| SUB | 0 | 0-9 | 0 | 0-9 | 00 | 0 |
| SBC | 0 | 0-8 | 1 | 6-F | FA | 0 |
| DEC | 1 | 7-F | 0 | 0-9 | A0 | 1 |
| NEG | 1 | 6-F | 1 | 6-F | 9A | 1 |
|------------------------------------------------------------------------------|
我必须说,我从来没有见过 dafter 指令规范。如果您仔细检查表格,您会发现指令的效果仅取决于C
andH
标志和累加器中的值——它根本不取决于前面的指令。此外,它不会透露如果,例如,,C=0
并且H=1
累加器中的低位数字是 4 或 5,会发生什么。因此,在这种情况下,您将不得不执行 a NOP
,或者生成错误消息或其他内容。
只是想补充一点,N 标志是他们在谈论之前的操作时的意思。加法设置 N = 0,减法设置 N = 1。因此,A 寄存器的内容和 C、H 和 N 标志确定结果。
该指令旨在支持 BCD 算术,但还有其他用途。考虑这段代码:
and 15
add a,90h
daa
adc a,40h
daa
它结束了将 A 寄存器的低 4 位转换为 ASCII 值 '0'、'1'、...'9'、'A'、'B'、...、'F'。换句话说,二进制到十六进制的转换器。
我发现这条指令也相当混乱,但我发现z80-heaven对其行为的描述最有帮助。
执行该指令时,使用标志的内容对 A 寄存器进行 BCD 校正。确切的过程如下:如果 A 的最低有效四位包含非 BCD 数字(即大于 9)或设置了 H 标志,则将 $06 添加到寄存器中。然后检查四个最高有效位。如果这个更重要的数字也恰好大于 9 或设置了 C 标志,则添加 $60。
这为指令提供了一个简单的模式:
此外,虽然 DAA旨在在加法或减法之后运行,但它可以随时运行。
这是生产中的代码,正确实现 DAA 并通过了 zexall/zexdoc/z80test Z80 操作码测试套件。
基于The Undocumented Z80 Documented,第 17-18 页。
void daa()
{
int t;
t=0;
// 4 T states
T(4);
if(flags.H || ((A & 0xF) > 9) )
t++;
if(flags.C || (A > 0x99) )
{
t += 2;
flags.C = 1;
}
// builds final H flag
if (flags.N && !flags.H)
flags.H=0;
else
{
if (flags.N && flags.H)
flags.H = (((A & 0x0F)) < 6);
else
flags.H = ((A & 0x0F) >= 0x0A);
}
switch(t)
{
case 1:
A += (flags.N)?0xFA:0x06; // -6:6
break;
case 2:
A += (flags.N)?0xA0:0x60; // -0x60:0x60
break;
case 3:
A += (flags.N)?0x9A:0x66; // -0x66:0x66
break;
}
flags.S = (A & BIT_7);
flags.Z = !A;
flags.P = parity(A);
flags.X = A & BIT_5;
flags.Y = A & BIT_3;
}
为了可视化 DAA 交互,出于调试目的,我编写了一个小型 Z80 汇编程序,可以在实际 ZX Spectrum 或准确模拟 DAA 的仿真中运行:https ://github.com/ruyrybeyro/daatable
至于它的行为方式,在使用上述汇编程序生成 DAA 之前和之后得到一个标志 N、C、H 和寄存器 A 的表:https ://github.com/ruyrybeyro/daatable/blob/master/daaoutput.txt