Урок 3. Отрицательные числа и вычитание: дополнительный код

В картинках

После этого урока тот же сумматор научится вычитать: тумблер SUB превращает A + B в A − B. Четыре лампы покажут флаги — то, что процессор знает о каждом ответе: был ли перенос, ноль ли ответ, отрицательный ли он и поместился ли он в 4 бита.


3.1. Идея: числа по кругу

Четырёхбитный сумматор считает по кругу: 15 + 1 даёт 0 — пятый разряд уходит в перенос. Значит, 1111 ведёт себя как −1: прибавь к нему 1 — получится 0. Так же 1110 — это −2, а 1000 — −8.

Договоримся: если старший бит 1, число отрицательное. Тогда 4 бита — это числа от −8 до 7. Такая запись называется дополнительный код. Сумматору ничего менять не надо: он складывает биты, а что они значат — решаем мы.

00000000110010200113010040101501106011171000-81001-71010-61011-51100-41101-31110-21111-1+1 — шагпо часовойстарший бит 0 — числа 0…7старший бит 1 — числа −8…−11111 + 1 = 1 0000в 4 битах: −1 + 1 = 0−3: перевернуть и +10011 → 1100 → 11010111 + 1 = 10007 + 1 = «−8»: переполнение V
Четыре бита — круг из 16 чисел: +1 — шаг по часовой стрелке, после 1111 снова 0000. Если считать числа со старшим битом 1 отрицательными, 1111 — это −1, 1000 — −8. Сумматор тот же, меняется только то, как мы читаем биты.

Как получить −B: перевернуть все биты B и прибавить 1. Например, 3 = 0011 → 1100 → 1101 = −3. Проверка: 3 + (−3) = 0011 + 1101 = 1 0000 — в 4 битах ноль.

А вычесть — значит прибавить −B:

  • биты B переворачивает XOR с тумблером SUB: при SUB = 0 бит проходит как есть, при SUB = 1 — наоборот;
  • «плюс 1» даёт тот же SUB, поданный на вход переноса CI сумматора.
SUB=1B0=1bx0=0B1=0bx1=1B2=1bx2=0B3=0bx3=1ADDER4A + bx + CICIA = 0011S = 1110= −2, N горит0011 + 1010 + 1= 1110
Вычитатель ADDSUB4 на примере 3 − 5. SUB = 1 переворачивает биты B в XOR (0101 → 1010) и через CI добавляет единицу: 3 + 1010 + 1 = 1110, то есть −2. При SUB = 0 XOR пропускают B как есть, CI = 0 — обычное сложение.

3.2. Флаги

Каждый процессор после сложения или вычитания запоминает четыре бита про ответ:

  • C (carry) — перенос из старшего разряда. При вычитании C = 1 значит «заёма не было»: A ≥ B без знака;
  • Z (zero) — ответ ноль: все разряды 0, это NOR всех битов;
  • N (negative) — ответ отрицательный: это просто старший бит суммы;
  • V (overflow) — ответ не поместился в числа со знаком: оба слагаемых одного знака, а сумма — другого. Например, 7 + 1 дают 1000 — «−8».

По этим флагам процессор принимает решения: «если ноль — перейти», «если меньше — повторить».

3.3. Сборка

Тот же проект. Стенд урока 2 (8 тумблеров, ADDER4 на холсте, три табло) удали; Power/Ground оставь.

Идея. Вычесть B — значит прибавить −B. В дополнительном коде −B = все биты B наоборот и плюс 1. Переворачивает XOR с тумблером SUB (при SUB = 0 бит проходит как есть, при SUB = 1 — наоборот), а «плюс 1» даёт тот же SUB на входе переноса CI сумматора.

Шаг 1. ADDSUB4 — сложение и вычитание

ДетальНогаМетка
Порт входавыходA0
Порт входавыходA1
Порт входавыходA2
Порт входавыходA3
Порт входавыходB0
Порт входавыходB1
Порт входавыходB2
Порт входавыходB3
Порт входавыходSUB
Порт входавыходCI
XOR (бит 0)I0
I1
O0
B0
SUB
bx0
XOR (бит 1)I0
I1
O0
B1
SUB
bx1
XOR (бит 2)I0
I1
O0
B2
SUB
bx2
XOR (бит 3)I0
I1
O0
B3
SUB
bx3
ADDER4A0
A1
A2
A3
B0
B1
B2
B3
CI
S0
S1
S2
S3
CO
A0
A1
A2
A3
bx0
bx1
bx2
bx3
CI
S0
S1
S2
S3
CO
XOR (знак A ≠ знак S)I0
I1
O0
A3
S3
va
XOR (знак B ≠ знак S)I0
I1
O0
bx3
S3
vb
AND (переполнение)I0
I1
O0
va
vb
V
Порт выходавходS0
Порт выходавходS1
Порт выходавходS2
Порт выходавходS3
Порт выходавходCO
Порт выходавходV

Ctrl+G → имя ADDSUB4. Ноги: входы A0–A3, B0–B3, SUB, CI; выходы S0–S3, CO, V.

V — переполнение: оба слагаемых одного знака, а сумма — другого (так 7 + 1 дают «−8»).

Шаг 2. Холст: тумблеры, вычитатель, флаги

ДетальНогаМетка
InputвыходA0
InputвыходA1
InputвыходA2
InputвыходA3
InputвыходB0
InputвыходB1
InputвыходB2
InputвыходB3
InputвыходSUB
ADDSUB4A0
A1
A2
A3
B0
B1
B2
B3
SUB
CI
S0
S1
S2
S3
CO
V
A0
A1
A2
A3
B0
B1
B2
B3
SUB
SUB
S0
S1
S2
S3
C
V
ORI0
I1
O0
S0
S1
z01
ORI0
I1
O0
S2
S3
z23
NOR (ноль)I0
I1
O0
z01
z23
Z
LED (C — перенос)входC
LED (Z — ноль)входZ
LED (N — знак)входS3
LED (V — переполнение)входV
Display⚙ширина 4, DEC, шина A
Display⚙ширина 4, DEC, шина B
Display⚙ширина 4, BIN, шина S

CI вычитателя — на ту же метку SUB: это и есть «плюс 1». Лампа N — просто старший бит суммы S3.

3.4. Проверка

Отрицательные числа в 4 битах: 1111 = −1, 1110 = −2 … 1000 = −8. Старший бит — знак.

AТумблеры A3…A0BТумблеры B3…B0SUBТабло SЛампыЭто
300114010000111—3 + 4 = 7
300115010111110N3 − 5 = −2
501015010110000Z, C5 − 5 = 0
200103001111111N2 − 3 = −1 (C = 0: был заём)
701111000101000N, V7 + 1 не влезает в 4 бита со знаком
910019100100010C, V−7 + −7 = −14 — тоже переполнение

В колонках «Тумблеры» слева — старший тумблер (A3), справа — A0; 1 — включён, 0 — выключен.

C при вычитании горит, когда заёма не было (A ≥ B без знака) — так у большинства процессоров, например 6502.

Если при SUB = 1 ответ на единицу меньше — проверь, что CI вычитателя на метке SUB, а не на GND.


Вставка урока 3 (Cmd+I)

Нужны XOR, AND, OR, NOR и ADDER4. Стенд урока 2 удали, кроме питания.

# Урок 3 — вычитание в дополнительном коде и флаги C, Z, N, V.# Вставка: Cmd+I поверх урока 2 (нужны XOR, AND, OR, NOR, ADDER4). Стенд урока 2 удалить, кроме питания. # --- сложение и вычитание: SUB = 1 переворачивает B и добавляет 1 через CI ---chip ADDSUB4 {  portIn(A0)  portIn(A1)  portIn(A2)  portIn(A3)  portIn(B0)  portIn(B1)  portIn(B2)  portIn(B3)  portIn(SUB)  portIn(CI)  portOut(S0)  portOut(S1)  portOut(S2)  portOut(S3)  portOut(CO)  portOut(V)  XOR(B0, SUB, bx0)  XOR(B1, SUB, bx1)  XOR(B2, SUB, bx2)  XOR(B3, SUB, bx3)  ADDER4(A0=A0, A1=A1, A2=A2, A3=A3, B0=bx0, B1=bx1, B2=bx2, B3=bx3, CI=CI, S0=S0, S1=S1, S2=S2, S3=S3, CO=CO)  # переполнение: знак результата не совпал со знаком обоих слагаемых  XOR(A3, S3, va)  XOR(bx3, S3, vb)  AND(va, vb, V)} # --- на холст: тумблеры A, B, SUB; вычитатель; флаги на лампах ---input(A0)input(A1)input(A2)input(A3)input(B0)input(B1)input(B2)input(B3)input(SUB)ADDSUB4(A0=A0, A1=A1, A2=A2, A3=A3, B0=B0, B1=B1, B2=B2, B3=B3, SUB=SUB, CI=SUB, S0=S0, S1=S1, S2=S2, S3=S3, CO=C, V=V)# Z: все разряды суммы — нольOR(S0, S1, z01)OR(S2, S3, z23)NOR(z01, z23, Z)led(C)led(Z)led(S3)led(V)

Не получается? Что-то не работает →