Флаги переполнения и переноса на Z80
Я дошел до реализации ADD A,r набора кодов операций на моем ядре 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 ответа
Биты результата получаются из усеченной суммы целых чисел без знака. Инструкция 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.
Мои и ваши способы вычисления флага переполнения верны. Фактически, оба они описаны в Руководстве пользователя ЦПУ Z80 в разделе "Флаги индикатора состояния 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) и результаты для него.
Еще один способ увидеть это, возможно, легче понять. При выполнении суммы:
- Знак всегда установлен на 7-й бит результата
- Ноль устанавливается, если результат 0x00
- Половина переноса устанавливается, когда выпадает правая сумма клевков операндов.
- Переполнение устанавливается, когда оба операнда положительны, а подписанная сумма отрицательна, или оба операнда отрицательны, а подписанная сумма положительна.
- Добавить /Sub сбрасывается
- Carry устанавливается, если сумма без знака переполняется 0xFF