Лисья Нора

Оглавление


§ Исторически сложилось

Представим то, что байт состоит из двух частей. Один байт содержит в себе 8 бит, и потому один байт можно разделить на две части – старшую часть из 4 бит и младшую из 4 бит, получая два ниббла – старший ниббл, который располагается в битах [7:4] и младший в битах [3:0]:
7 6 5 4 | 3 2 1 0 БИТЫ
========+=========
HIGH | LOW НИББЛ
Не только инженеры, но и программисты задумались: а что если в каждый ниббл записать число от 0 до 9? Да, мы знаем, что количество символов в одном ниббле больше: от 0 до 15 включительно. Но программисты прошлого думали так:
Компьютеры того времени (начиная 1970-х) были очень медленными, к примеру, первый процессор от Intel 4004, который был представлен планетарной публике 15 ноября 1971 года до Рождества Христова, имел лишь 4х битную шину данных. Его скорость варьировалась от 500 кГц до 740 кГц и об этом говорится в глобальной энциклопедии.
Цель процессора состояла в том, чтобы он выполнял функции калькулятор, а скорость была не то чтобы очень быстрая, а очень медленная. Итак, мы значит, храним десятичные цифры в каждом ниббле. Все так, но...

§ Есть нюанс

Что если к числу 9 добавить единицу? Правильно, получится 10. Только 10 получится не в виде 10, а в виде 0A, потому что, на самом деле, счет ведется в двоичной системе счисления (binary).
И что делать? Ведь этот результат совершенно неправильный в том формате, в котором он был должен быть. Для этого инженеры придумали в процессор добавить такую инструкцию как DAA (Decimal Adjust after Add – десятичная коррекция после сложения) и DAS (после вычитания). Эта инструкция проверяла следующие условия:
Если одно из этих условий выполняется, то инструкция корректирует результат. Для примера 9+1=0Ah инструкция добавит +6 чтобы получилось 10h. А что если мы сложим числа, допустим 9+8? Получится число 11h, что вроде как правильно, но не правильно. Почему? Потому что вроде как оба ниббла в порядке, а результат не тот.
Однако, у инструкции еще есть условие. Был ли перенос из 3-го в 4-й? Был. А как это узнать? Чтобы это узнать, надо поставить какой-то более-менее захудалый флаг после выполнения инструкции ADD и этот флаг как раз и есть флаг AF (Aux Flag). Если флаг переноса установлен, то добавляем +6 и получаем корректный (с точки зрения десятичного представления) ответ 17h.
С приходом Intel Pentium Phenom и Intel Core Ultra Series 3 (Panther Lake) флаг полупереноса неизбежно забыт в веках, а сама инструкция, которая его обрабатывает, канула в Лету в 64х битном режиме работы процессора.

§ Принципы счета

Есть два метода определения бита переноса. Один из них правильный, второй тоже правильный.
Если с первым методом всё кристально ясно, то со вторым интереснее, но в то же время, немного непонятно, почему так получается.
Но на самом деле, все слишком просто, если понимать принципы двоичного счета:
7 6 5 | 4 | 3 2 1 0
A 0 0 0 | a | 0 0 0 0
+ ------+---+---------
B 0 0 0 | b | 0 0 0 0
= ------+---+---------
C 0 0 0 | c | 0 0 0 0
Я намеренно выделил 4-й бит. Теперь объясню, почему. В обычных условиях, при сложении битов a + b мы получаем c по следующей логической функции: c = a xor b. Почему это так, я рассказывал в предыдущей главе, разбирая флаг чётности.
Суть вот в чем. Если при сложении a + b получится c, то это будет означать, что из 3-го разряда не было переноса, и это логично. Если бы перенос был, то нам надо было бы его учесть при сложении, но мы его при сложении не учитываем, так что результат логической функции a xor b не будет равен c, потому что перенос из предыдущего разряда неизбежно инвертирует разряд (ибо X xor 1 это инверсия X).
Для того чтобы определить, равен или не равен результат a xor b к результату c, сделаем (a xor b) xor c, и если результат будет равен (например 0 = 0 или 1 = 1), то ответ будет 0. Это правильный ответ, поскольку в случае равенства результата переноса из 3-го разряда не было, и это гарантированно. То есть да, это всё что надо сделать, больше никаких "подводных камней нет".
Для вычитания все работает в точности так же. Даже ничего менять не надо, потому что это так же как и сложение: 0-1=1, 1-1=0, 1-0=1, 0-0=0 в случае если заема не было. Если заем был, то это нарушает баланс и результат AF становится равным 1.