Флаги переполнения и переноса на Z80 - PullRequest
22 голосов
/ 07 ноября 2011

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

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

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

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

Ответы [ 2 ]

32 голосов
/ 07 ноября 2011

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

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

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

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

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

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

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

И мой, и ваш способ вычисления флага переполнения верны. Фактически, оба они описаны в Руководстве пользователя ЦП *1015* в разделе «Флаги индикатора состояния Z80».

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

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

РЕДАКТИРОВАТЬ: Я добавил функцию для вычитания с заимствованием (инструкция SBC / SBB) и результаты для него.

5 голосов
/ 23 мая 2015

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

  • Знак всегда установлен в бит 7 результата
  • Ноль устанавливается, если результат равен 0x00
  • Половина переноса устанавливается при переполнении суммы правого куска операндов
  • Переполнение устанавливается, когда оба операнда положительны, а сумма со знаком отрицательна, или оба операнда отрицательны, а сумма со знаком положительна
  • Add / Sub сбрасывается
  • Carry устанавливается, если сумма без знака переполняется 0xFF
...