Переполнение и перенос флагов на Z80

Я добрался до реализации набора кодов ADD A,r на моем ядре Z80. У меня была небольшая путаница с флагами переноса и переполнения, которые, я думаю, я прибил, но я хотел поставить его в сообщество, чтобы проверить, что я прав.

в основном, из того, что я вижу, ALU в Z80 не заботится о подписанных/неподписанных операциях, он просто добавляет биты. Это означает, что если два 8-разрядных значения добавляются вместе и вызывают 9-разрядное значение в результате их добавления, перенос флаг будет установлен. Это включает в себя добавление двух отрицательных двух дополнительных чисел, например -20 (11101100) и -40 (11011000), поскольку, хотя результат -60 (11000100), результат на самом деле является 9-разрядным значением 1 1100 0100. Это, безусловно, означает, что если добавить два отрицательных значения дополнения two, флаг переноса всегда будет установлен, даже если нет условия переполнения - я прав?

во-вторых, я решил, что для обнаружения переполнения в этой инструкции я бы XOR бит 7 обоих операндов, и если результат был 10000000, то определенно нет переполнения - если результат этого 00000000, то может быть переполнение, поскольку знаки одинаковы, и поэтому я бы XOR бит 7 результата сложения с битом 7 любого операнда, и если результат этого 10000000, то произошло переполнение, и я установил флаг переполнения P/V. Я тоже здесь?

извините за такой запутанный вопрос, я уверен, что я прав, но мне нужно знать, прежде чем я продолжу бесчисленное множество инструкций, основанных на этой логике. Большое спасибо.

2 ответов


биты результата получаются из усеченной суммы целых чисел без знака. Инструкция add не заботится о знаке здесь и не заботится о вашей собственной интерпретации целых чисел как подписанных или неподписанных. Он просто добавляет, как если бы цифры были без знака.

флаг переноса (или заимствование в случае вычитания)-это несуществующий 9-й бит из добавления 8-битных целых чисел без знака. Фактически, этот флаг означает переполнение / underflow для add / sub целое без знака. Опять же, add не заботится о знаках здесь вообще, он просто добавляет, как будто числа были без знака.

добавление двух отрицательных номеров дополнения 2 приведет к установке флага переноса на 1, Правильно.

флаг переполнения показывает, было ли переполнение / underflow для добавления / sub целых чисел со знаком. Чтобы установить флаг переполнения, инструкция обрабатывает числа как подписанные (так же, как она рассматривает их как неподписанные для флага переноса и 8 бит о результате).

идея установки флага переполнения проста. Предположим, вы подписываете-расширяете свои 8-битные целые числа со знаком до 9 бит, то есть просто копируете 7-й бит в дополнительный 8-й бит. Переполнение / подток произойдет, если 9-разрядная сумма / разность этих 9-разрядных целых чисел со знаком имеет разные значения в битах 7 и 8, что означает, что сложение / вычитание потеряло знак результата в 7-м бит и использовало его для величины результата, или, другими словами, 8 бит не могут приспособьте знак бит и такой большой величины.

теперь бит 7 результата может отличаться от мнимого знака бит 8, если и только если перенос в бит 7 и перенос в бит 8 (=перенос из бит 7) различны. Это потому, что мы начинаем с добавлений, имеющих бит 7=бит 8, и только разные переноски в них могут влиять на них в результате по-разному.

таким образом, флаг переполнения = флаг выполнения XOR переносится из бита 6 в бит 7.

оба мои и ваши способы вычисления флага переполнения верны. На самом деле, оба описываются в пользователей на Z80 процессора!--22--> в разделе "флаги индикатора состояния Z80".

вот как вы можете эмулировать большую часть инструкции АЦП в C, где у вас нет прямого доступа к флагам процессора и вы не можете в полной мере воспользоваться инструкцией АЦП эмулирующего процессора:

#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 different
  {
    // 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 to #if 1 использовать метод на основе сравнения знаков для расчета переполнения. Результат будет тот же. На первый взгляд это немного удивительно, что знак на основе метода заботится о ручной клади тоже.

обратите внимание, что, используя мой метод, в котором я вычисляю все переноски в битах от 0 до 7, вы также получите бесплатно значение half-carry флаг (перенос с бита 3 на бит 4), который необходим для DAA инструкция.

EDIT: я добавил функцию для вычитания с заимствованием (инструкция SBC/SBB) и результатами для него.


другой способ увидеть это, который, возможно, легче понять. При выполнении суммы:

  • знак всегда устанавливается в бит 7 результата
  • ноль устанавливается, если результат 0x00
  • половина-carry устанавливается, когда правая сумма кусочков операндов переполняется
  • переполнения устанавливается, когда оба операнда положительны, а подписанная сумма отрицательна или оба операнда отрицательны и подписанная сумма положительна
  • Добавить / Sub сброс
  • нести устанавливается, если сумма без знака переполняет 0xFF