【问题标题】:Overflow and Carry flags on Z80Z80 上的溢出和进位标志
【发布时间】:2011-11-07 09:16:24
【问题描述】:

我已经开始在我的 Z80 内核上实现 ADD A,r 组操作码。我对我认为我已经确定的进位和溢出标志有点困惑,但我想把它提交给社区以检查我是否正确。

基本上,据我所知,Z80 中的 ALU 并不关心有符号/无符号操作,它只是添加位。这意味着如果将两个 8 位值相加并作为相加的结果产生一个 9 位值,则将设置进位标志。这包括添加两个负的二进制补码,例如 -20 (11101100) 和 -40 (11011000),虽然结果是 -60 (11000100),但结果实际上是一个 9 位值 1 1100 0100。这肯定意味着如果添加两个负的二进制补码值,则将始终设置进位标志,即使没有溢出条件 - 对吗?

其次,我决定要检测该指令中的溢出,我将对两个操作数的第 7 位进行 XOR,如果结果是 10000000,那么肯定没有溢出 - 如果结果是 00000000,那么可能有溢出,因为符号相同,因此我会将加法结果的第 7 位与任一操作数的第 7 位进行异或,如果结果为 10000000,则发生溢出,我设置 P/V 溢出旗帜。我也在这里吗?

很抱歉提出如此复杂的问题,我很确定我是对的,但在我继续基于此逻辑的无数更多指令之前,我需要知道。非常感谢。

【问题讨论】:

    标签: math assembly z80 carryflag signed-overflow


    【解决方案1】:

    结果的位是从无符号整数的截断和中获得的。 add 指令不关心这里的符号,也不关心您自己将整数解释为有符号还是无符号。它只是像数字未签名一样添加。

    进位标志(或在减法的情况下借位)是 8 位无符号整数相加后不存在的第 9 位。实际上,此标志表示无符号整数的加/减的上溢/下溢。同样,add 根本不关心这里的符号,它只是像数字没有符号一样添加。

    添加两个负 2 的补数将导致进位标志设置为 1,正确。

    溢出标志显示有符号整数的加/减是否存在上溢/下溢。为了设置溢出标志,指令将数字视为有符号(就像它对进位标志和结果的 8 位将它们视为无符号一样)。

    设置溢出标志的想法很简单。假设您将 8 位有符号整数符号扩展为 9 位,也就是说,只需将第 7 位复制到额外的第 8 位。如果这些 9 位有符号整数的 9 位和/差在第 7 位和第 8 位具有不同的值,则会发生上溢/下溢,这意味着加法/减法在第 7 位中丢失了结果的符号并将其用于结果的大小,或者说,8位无法容纳符号位和这么大的大小。

    现在,当且仅当第 7 位的进位和第 8 位的进位(=第 7 位的进位)不同时,结果的第 7 位可能与虚数符号位 8 不同。这是因为我们从第 7 位 = 第 8 位的加数开始,只有不同的进位才能以不同的方式影响结果。

    所以溢出标志 = 进位标志 XOR 从第 6 位进位到第 7 位。

    我和你计算溢出标志的方法都是正确的。事实上,两者都在“Z80 状态指示器标志”部分的Z80 CPU User's Manual 中进行了描述。

    以下是在 C 语言中模拟大多数 ADC 指令的方法,您无法直接访问 CPU 的标志,也无法充分利用模拟 CPU 的 ADC 指令:

    #include <stdio.h>
    #include <limits.h>
    
    #if CHAR_BIT != 8
    #error char expected to have exactly 8 bits.
    #endif
    
    typedef unsigned char uint8;
    typedef signed char int8;
    
    #define FLAGS_CY_SHIFT 0
    #define FLAGS_OV_SHIFT 1
    #define FLAGS_CY_MASK  (1 << FLAGS_CY_SHIFT)
    #define FLAGS_OV_MASK  (1 << FLAGS_OV_SHIFT)
    
    void Adc(uint8* acc, uint8 b, uint8* flags)
    {
      uint8 a = *acc;
      uint8 carryIns;
      uint8 carryOut;
    
      // Calculate the carry-out depending on the carry-in and addends.
      //
      // carry-in = 0: carry-out = 1 IFF (a + b > 0xFF) or,
      //   equivalently, but avoiding overflow in C: (a > 0xFF - b).
      //
      // carry-in = 1: carry-out = 1 IFF (a + b + 1 > 0xFF) or,
      //   equivalently, (a + b >= 0xFF) or,
      //   equivalently, but avoiding overflow in C: (a >= 0xFF - b).
      //
      // Also calculate the sum bits.
      if (*flags & FLAGS_CY_MASK)
      {
        carryOut = (a >= 0xFF - b);
        *acc = a + b + 1;
      }
      else
      {
        carryOut = (a > 0xFF - b);
        *acc = a + b;
      }
    
    #if 0
      // Calculate the overflow by sign comparison.
      carryIns = ((a ^ b) ^ 0x80) & 0x80;
      if (carryIns) // if addend signs are the same
      {
        // overflow if the sum sign differs from the sign of either of addends
        carryIns = ((*acc ^ a) & 0x80) != 0;
      }
    #else
      // Calculate all carry-ins.
      // Remembering that each bit of the sum =
      //   addend a's bit XOR addend b's bit XOR carry-in,
      // we can work out all carry-ins from a, b and their sum.
      carryIns = *acc ^ a ^ b;
    
      // Calculate the overflow using the carry-out and
      // most significant carry-in.
      carryIns = (carryIns >> 7) ^ carryOut;
    #endif
    
      // Update flags.
      *flags &= ~(FLAGS_CY_MASK | FLAGS_OV_MASK);
      *flags |= (carryOut << FLAGS_CY_SHIFT) | (carryIns << FLAGS_OV_SHIFT);
    }
    
    void Sbb(uint8* acc, uint8 b, uint8* flags)
    {
      // a - b - c = a + ~b + 1 - c = a + ~b + !c
      *flags ^= FLAGS_CY_MASK;
      Adc(acc, ~b, flags);
      *flags ^= FLAGS_CY_MASK;
    }
    
    const uint8 testData[] =
    {
      0,
      1,
      0x7F,
      0x80,
      0x81,
      0xFF
    };
    
    int main(void)
    {
      unsigned aidx, bidx, c;
    
      printf("ADC:\n");
      for (c = 0; c <= 1; c++)
        for (aidx = 0; aidx < sizeof(testData)/sizeof(testData[0]); aidx++)
          for (bidx = 0; bidx < sizeof(testData)/sizeof(testData[0]); bidx++)
          {
            uint8 a = testData[aidx];
            uint8 b = testData[bidx];
            uint8 flags = c << FLAGS_CY_SHIFT;
            printf("%3d(%4d) + %3d(%4d) + %u = ",
                   a, (int8)a, b, (int8)b, c);
            Adc(&a, b, &flags);
            printf("%3d(%4d) CY=%d OV=%d\n",
                   a, (int8)a, (flags & FLAGS_CY_MASK) != 0, (flags & FLAGS_OV_MASK) != 0);
          }
    
      printf("SBB:\n");
      for (c = 0; c <= 1; c++)
        for (aidx = 0; aidx < sizeof(testData)/sizeof(testData[0]); aidx++)
          for (bidx = 0; bidx < sizeof(testData)/sizeof(testData[0]); bidx++)
          {
            uint8 a = testData[aidx];
            uint8 b = testData[bidx];
            uint8 flags = c << FLAGS_CY_SHIFT;
            printf("%3d(%4d) - %3d(%4d) - %u = ",
                   a, (int8)a, b, (int8)b, c);
            Sbb(&a, b, &flags);
            printf("%3d(%4d) CY=%d OV=%d\n",
                   a, (int8)a, (flags & FLAGS_CY_MASK) != 0, (flags & FLAGS_OV_MASK) != 0);
          }
    
      return 0;
    }
    

    输出:

    ADC:
      0(   0) +   0(   0) + 0 =   0(   0) CY=0 OV=0
      0(   0) +   1(   1) + 0 =   1(   1) CY=0 OV=0
      0(   0) + 127( 127) + 0 = 127( 127) CY=0 OV=0
      0(   0) + 128(-128) + 0 = 128(-128) CY=0 OV=0
      0(   0) + 129(-127) + 0 = 129(-127) CY=0 OV=0
      0(   0) + 255(  -1) + 0 = 255(  -1) CY=0 OV=0
      1(   1) +   0(   0) + 0 =   1(   1) CY=0 OV=0
      1(   1) +   1(   1) + 0 =   2(   2) CY=0 OV=0
      1(   1) + 127( 127) + 0 = 128(-128) CY=0 OV=1
      1(   1) + 128(-128) + 0 = 129(-127) CY=0 OV=0
      1(   1) + 129(-127) + 0 = 130(-126) CY=0 OV=0
      1(   1) + 255(  -1) + 0 =   0(   0) CY=1 OV=0
    127( 127) +   0(   0) + 0 = 127( 127) CY=0 OV=0
    127( 127) +   1(   1) + 0 = 128(-128) CY=0 OV=1
    127( 127) + 127( 127) + 0 = 254(  -2) CY=0 OV=1
    127( 127) + 128(-128) + 0 = 255(  -1) CY=0 OV=0
    127( 127) + 129(-127) + 0 =   0(   0) CY=1 OV=0
    127( 127) + 255(  -1) + 0 = 126( 126) CY=1 OV=0
    128(-128) +   0(   0) + 0 = 128(-128) CY=0 OV=0
    128(-128) +   1(   1) + 0 = 129(-127) CY=0 OV=0
    128(-128) + 127( 127) + 0 = 255(  -1) CY=0 OV=0
    128(-128) + 128(-128) + 0 =   0(   0) CY=1 OV=1
    128(-128) + 129(-127) + 0 =   1(   1) CY=1 OV=1
    128(-128) + 255(  -1) + 0 = 127( 127) CY=1 OV=1
    129(-127) +   0(   0) + 0 = 129(-127) CY=0 OV=0
    129(-127) +   1(   1) + 0 = 130(-126) CY=0 OV=0
    129(-127) + 127( 127) + 0 =   0(   0) CY=1 OV=0
    129(-127) + 128(-128) + 0 =   1(   1) CY=1 OV=1
    129(-127) + 129(-127) + 0 =   2(   2) CY=1 OV=1
    129(-127) + 255(  -1) + 0 = 128(-128) CY=1 OV=0
    255(  -1) +   0(   0) + 0 = 255(  -1) CY=0 OV=0
    255(  -1) +   1(   1) + 0 =   0(   0) CY=1 OV=0
    255(  -1) + 127( 127) + 0 = 126( 126) CY=1 OV=0
    255(  -1) + 128(-128) + 0 = 127( 127) CY=1 OV=1
    255(  -1) + 129(-127) + 0 = 128(-128) CY=1 OV=0
    255(  -1) + 255(  -1) + 0 = 254(  -2) CY=1 OV=0
      0(   0) +   0(   0) + 1 =   1(   1) CY=0 OV=0
      0(   0) +   1(   1) + 1 =   2(   2) CY=0 OV=0
      0(   0) + 127( 127) + 1 = 128(-128) CY=0 OV=1
      0(   0) + 128(-128) + 1 = 129(-127) CY=0 OV=0
      0(   0) + 129(-127) + 1 = 130(-126) CY=0 OV=0
      0(   0) + 255(  -1) + 1 =   0(   0) CY=1 OV=0
      1(   1) +   0(   0) + 1 =   2(   2) CY=0 OV=0
      1(   1) +   1(   1) + 1 =   3(   3) CY=0 OV=0
      1(   1) + 127( 127) + 1 = 129(-127) CY=0 OV=1
      1(   1) + 128(-128) + 1 = 130(-126) CY=0 OV=0
      1(   1) + 129(-127) + 1 = 131(-125) CY=0 OV=0
      1(   1) + 255(  -1) + 1 =   1(   1) CY=1 OV=0
    127( 127) +   0(   0) + 1 = 128(-128) CY=0 OV=1
    127( 127) +   1(   1) + 1 = 129(-127) CY=0 OV=1
    127( 127) + 127( 127) + 1 = 255(  -1) CY=0 OV=1
    127( 127) + 128(-128) + 1 =   0(   0) CY=1 OV=0
    127( 127) + 129(-127) + 1 =   1(   1) CY=1 OV=0
    127( 127) + 255(  -1) + 1 = 127( 127) CY=1 OV=0
    128(-128) +   0(   0) + 1 = 129(-127) CY=0 OV=0
    128(-128) +   1(   1) + 1 = 130(-126) CY=0 OV=0
    128(-128) + 127( 127) + 1 =   0(   0) CY=1 OV=0
    128(-128) + 128(-128) + 1 =   1(   1) CY=1 OV=1
    128(-128) + 129(-127) + 1 =   2(   2) CY=1 OV=1
    128(-128) + 255(  -1) + 1 = 128(-128) CY=1 OV=0
    129(-127) +   0(   0) + 1 = 130(-126) CY=0 OV=0
    129(-127) +   1(   1) + 1 = 131(-125) CY=0 OV=0
    129(-127) + 127( 127) + 1 =   1(   1) CY=1 OV=0
    129(-127) + 128(-128) + 1 =   2(   2) CY=1 OV=1
    129(-127) + 129(-127) + 1 =   3(   3) CY=1 OV=1
    129(-127) + 255(  -1) + 1 = 129(-127) CY=1 OV=0
    255(  -1) +   0(   0) + 1 =   0(   0) CY=1 OV=0
    255(  -1) +   1(   1) + 1 =   1(   1) CY=1 OV=0
    255(  -1) + 127( 127) + 1 = 127( 127) CY=1 OV=0
    255(  -1) + 128(-128) + 1 = 128(-128) CY=1 OV=0
    255(  -1) + 129(-127) + 1 = 129(-127) CY=1 OV=0
    255(  -1) + 255(  -1) + 1 = 255(  -1) CY=1 OV=0
    SBB:
      0(   0) -   0(   0) - 0 =   0(   0) CY=0 OV=0
      0(   0) -   1(   1) - 0 = 255(  -1) CY=1 OV=0
      0(   0) - 127( 127) - 0 = 129(-127) CY=1 OV=0
      0(   0) - 128(-128) - 0 = 128(-128) CY=1 OV=1
      0(   0) - 129(-127) - 0 = 127( 127) CY=1 OV=0
      0(   0) - 255(  -1) - 0 =   1(   1) CY=1 OV=0
      1(   1) -   0(   0) - 0 =   1(   1) CY=0 OV=0
      1(   1) -   1(   1) - 0 =   0(   0) CY=0 OV=0
      1(   1) - 127( 127) - 0 = 130(-126) CY=1 OV=0
      1(   1) - 128(-128) - 0 = 129(-127) CY=1 OV=1
      1(   1) - 129(-127) - 0 = 128(-128) CY=1 OV=1
      1(   1) - 255(  -1) - 0 =   2(   2) CY=1 OV=0
    127( 127) -   0(   0) - 0 = 127( 127) CY=0 OV=0
    127( 127) -   1(   1) - 0 = 126( 126) CY=0 OV=0
    127( 127) - 127( 127) - 0 =   0(   0) CY=0 OV=0
    127( 127) - 128(-128) - 0 = 255(  -1) CY=1 OV=1
    127( 127) - 129(-127) - 0 = 254(  -2) CY=1 OV=1
    127( 127) - 255(  -1) - 0 = 128(-128) CY=1 OV=1
    128(-128) -   0(   0) - 0 = 128(-128) CY=0 OV=0
    128(-128) -   1(   1) - 0 = 127( 127) CY=0 OV=1
    128(-128) - 127( 127) - 0 =   1(   1) CY=0 OV=1
    128(-128) - 128(-128) - 0 =   0(   0) CY=0 OV=0
    128(-128) - 129(-127) - 0 = 255(  -1) CY=1 OV=0
    128(-128) - 255(  -1) - 0 = 129(-127) CY=1 OV=0
    129(-127) -   0(   0) - 0 = 129(-127) CY=0 OV=0
    129(-127) -   1(   1) - 0 = 128(-128) CY=0 OV=0
    129(-127) - 127( 127) - 0 =   2(   2) CY=0 OV=1
    129(-127) - 128(-128) - 0 =   1(   1) CY=0 OV=0
    129(-127) - 129(-127) - 0 =   0(   0) CY=0 OV=0
    129(-127) - 255(  -1) - 0 = 130(-126) CY=1 OV=0
    255(  -1) -   0(   0) - 0 = 255(  -1) CY=0 OV=0
    255(  -1) -   1(   1) - 0 = 254(  -2) CY=0 OV=0
    255(  -1) - 127( 127) - 0 = 128(-128) CY=0 OV=0
    255(  -1) - 128(-128) - 0 = 127( 127) CY=0 OV=0
    255(  -1) - 129(-127) - 0 = 126( 126) CY=0 OV=0
    255(  -1) - 255(  -1) - 0 =   0(   0) CY=0 OV=0
      0(   0) -   0(   0) - 1 = 255(  -1) CY=1 OV=0
      0(   0) -   1(   1) - 1 = 254(  -2) CY=1 OV=0
      0(   0) - 127( 127) - 1 = 128(-128) CY=1 OV=0
      0(   0) - 128(-128) - 1 = 127( 127) CY=1 OV=0
      0(   0) - 129(-127) - 1 = 126( 126) CY=1 OV=0
      0(   0) - 255(  -1) - 1 =   0(   0) CY=1 OV=0
      1(   1) -   0(   0) - 1 =   0(   0) CY=0 OV=0
      1(   1) -   1(   1) - 1 = 255(  -1) CY=1 OV=0
      1(   1) - 127( 127) - 1 = 129(-127) CY=1 OV=0
      1(   1) - 128(-128) - 1 = 128(-128) CY=1 OV=1
      1(   1) - 129(-127) - 1 = 127( 127) CY=1 OV=0
      1(   1) - 255(  -1) - 1 =   1(   1) CY=1 OV=0
    127( 127) -   0(   0) - 1 = 126( 126) CY=0 OV=0
    127( 127) -   1(   1) - 1 = 125( 125) CY=0 OV=0
    127( 127) - 127( 127) - 1 = 255(  -1) CY=1 OV=0
    127( 127) - 128(-128) - 1 = 254(  -2) CY=1 OV=1
    127( 127) - 129(-127) - 1 = 253(  -3) CY=1 OV=1
    127( 127) - 255(  -1) - 1 = 127( 127) CY=1 OV=0
    128(-128) -   0(   0) - 1 = 127( 127) CY=0 OV=1
    128(-128) -   1(   1) - 1 = 126( 126) CY=0 OV=1
    128(-128) - 127( 127) - 1 =   0(   0) CY=0 OV=1
    128(-128) - 128(-128) - 1 = 255(  -1) CY=1 OV=0
    128(-128) - 129(-127) - 1 = 254(  -2) CY=1 OV=0
    128(-128) - 255(  -1) - 1 = 128(-128) CY=1 OV=0
    129(-127) -   0(   0) - 1 = 128(-128) CY=0 OV=0
    129(-127) -   1(   1) - 1 = 127( 127) CY=0 OV=1
    129(-127) - 127( 127) - 1 =   1(   1) CY=0 OV=1
    129(-127) - 128(-128) - 1 =   0(   0) CY=0 OV=0
    129(-127) - 129(-127) - 1 = 255(  -1) CY=1 OV=0
    129(-127) - 255(  -1) - 1 = 129(-127) CY=1 OV=0
    255(  -1) -   0(   0) - 1 = 254(  -2) CY=0 OV=0
    255(  -1) -   1(   1) - 1 = 253(  -3) CY=0 OV=0
    255(  -1) - 127( 127) - 1 = 127( 127) CY=0 OV=1
    255(  -1) - 128(-128) - 1 = 126( 126) CY=0 OV=0
    255(  -1) - 129(-127) - 1 = 125( 125) CY=0 OV=0
    255(  -1) - 255(  -1) - 1 = 255(  -1) CY=1 OV=0
    

    您可以将#if 0 更改为#if 1 以使用基于符号比较的方法进行溢出计算。结果将是相同的。乍一看,基于符号的方法也会处理进位,这有点令人惊讶。

    请注意,通过使用我将所有进位计算到位 0 到 7 的方法,您还可以免费获得DAA 指令。

    编辑:我添加了一个借位减法函数(SBC/SBB 指令)及其结果。

    【讨论】:

    • 这是完美的 - 非常感谢 :-) 我知道我的思路是正确的。也感谢您的代码示例。我实际上正在使用 Java(因为我的目标是在它完成后成为一个完全跨平台的主系统模拟器),尽管我可以理解足够的 C 来了解你在做什么。抱歉,如果我的问题看起来很简单,只是我在进行这个项目时必须自学很多二进制数学,但到目前为止我似乎已经掌握了它:-)
    • 谢谢你的解释,真的很准确。我通过halfCarryOut = carryIn ? ((a &amp; 0x0F) &gt;= 0x0F - (a &amp; 0x0F)) : ((a &amp; 0x0F) &gt; 0x0F - (a &amp; 0x0F)); halfCarryOut = ((res ^ a ^ b) &gt;&gt; 4) ^ halfCarryOut;找出了半进位标志,应该是正确的。
    • @Jack 如果你已经测试过它并且它可以工作,好的(我不打算验证它)。但正如我在答案末尾指出的那样,它可以做得更简单。在#else 和#endif 之间使用代码变体。在carryIns = *acc ^ a ^ b; 之后做halfCarryOut = (carryIns &gt;&gt; 4) &amp; 1;,这就是你需要添加的所有内容。
    • 是的,我的整个代码完全不同,所以我只是重新调整了我的实现来写我的评论。不,我不打算要求任何验证,我正在尝试完成您的答案以供将来参考:)
    • 对于那些实施 GAMEBOY Z80 的人:我只是想在其中添加我的测试。在Z80的gameboy版本中,255+1操作会产生一个半进位标志和一个进位标志。
    【解决方案2】:

    另一种可能更容易理解的方式。求和时:

    • 符号始终设置为结果的第 7 位
    • 如果结果为 0x00,则设置
    • 半进位在操作数的右半字节和溢出时设置
    • 溢出在两个有符号操作数均为正且有符号和为负或两个有符号操作数均为负且有符号和为正时设置
    • 添加/订阅已重置
    • 进位 如果无符号和溢出 0xFF
    • 则设置

    【讨论】:

    • 很好的回顾。进一步澄清一下:“加/减被重置”,因为N 只有在最后一个操作是减法时才会显式地 set 。 (我认为这仅用于DAA 指令。)
    • “第 7 位”是什么意思? 7、倒数,从0开始?
    猜你喜欢
    • 2016-02-05
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-03
    • 2018-06-19
    相关资源
    最近更新 更多