Код. Тайный язык информатики — страница 44 из 71

OR A, H

A5

AND A, L

B5

OR A, L

A6

AND A,[HL]

B6

OR A,[HL]

A7

AND A, A

B7

OR A, A

A8

XOR A, B

B8

CMP A, B

A9

XOR A, C

B9

CMP A, C

AA

XOR A, D

BA

CMP A, D

AB

XOR A, E

BB

CMP A, E

AC

XOR A, H

BC

CMP A, H

AD

XOR A, L

BD

CMP A, L

AE

XOR A,[HL]

BE

CMP A,[HL]

AF

XOR A, A

BF

CMP A, A

Команды AND, OR и XOR выполняются побитово, то есть отдельно над каждой парой битов. Например, в результате выполнения следующих команд значение в аккумуляторе будет равно 05h.

MVI A,0Fh

MVI B,55h

AND A, B

Если бы последней была команда OR, то результат был бы равен 5Fh; если бы последней была команда XOR — 5Ah.

Команда CMP (Compare — сравнить) аналогична команде SUB, за исключением того, что результат не сохраняется в аккумуляторе. Другими словами, команда CMP выполняет вычитание, а затем удаляет результат. В чем же смысл? Во флагах! Флаги говорят о том, как два сравниваемых байта соотносятся. Рассмотрим, например, следующие команды.

MVI B,25h

CMP A, B

После их выполнения содержимое аккумулятора (А) остается прежним. Если значение в A равно 25h, будет установлен флаг нуля, а если значение в A меньше 25h — флаг переноса.

Для восьми арифметических и логических операций также существуют версии, которые выполняются непосредственно над байтами.

Код

Команда

Код

Команда

C6

ADI A, xx

E6

ANI A, xx

CE

ACI A, xx

EE

XRI A, xx

D6

SUI A, xx

F6

ORI A, xx

DE

SBI A, xx

FE

CPI A, xx

Например, две приведенные выше строки можно заменить следующей.

CPI A,25h

Вот еще две команды для процессора 8080.

Код

Команда

27

DAA

2F

CMA

Команда CMA (Complement Accumulator — дополнить аккумулятор) выполняет дополнение значения в аккумуляторе до 1. Каждый 0 обращается в 1, а 1 — в 0. Если в аккумуляторе содержится значение 01100101, то после исполнения команды CMA в нем будет содержаться значение 10011010. Этого же результата можно достичь и с помощью следующей команды.

XRI A, FFh

Упомянутая выше команда DAA (Decimal Adjust Accumulator — десятичная коррекция аккумулятора), вероятно, является самой сложной в наборе команд процессора 8080. Специально для нее в микропроцессоре предусмотрено небольшое устройство.

DAA помогает программисту выполнять арифметические операции над десятичными числами в кодировке BCD (binary-coded decimal — десятичное в двоичной кодировке), где каждая тетрада может принимать значение только в диапазоне от 0000 до 1001, то есть от 0 до 9 в десятичном выражении. В формате BCD в восьми битах байта могут храниться две десятичные цифры.

Предположим, что в аккумуляторе содержится BCD-значение 27h, которое фактически соответствует десятичному значению 27, а в регистре B содержится BCD-значение 94h. (Обычно шестнадцатеричное значение 27h эквивалентно десятичному значению 39.) В результате выполнения следующих команд в аккумуляторе будет содержаться значение BBh, которое, разумеется, не является BCD-значением, поскольку в кодировке BCD-значение тетрады не может превышать 9.

MVI A,27h

MVI B,94h

ADD A, B

Однако при выполнении команды DAA в аккумулятор помещается значение 21h и устанавливается флаг переноса, поскольку сумма десятичных чисел 27 и 94 равна 121. Эта команда может пригодиться для арифметических операций над числами в кодировке BCD.

Часто возникает необходимость в прибавлении 1 к значению или в вычитании 1 из значения. В программе для выполнения умножения, описанной в главе 17, нужно вычесть из значения 1, и мы делали это, прибавляя значение FFh, которое является дополнением до 2 числа –1. Процессор 8080 предусматривает специальные команды для увеличения на 1 (инкрементирования) и уменьшения на 1 (декрементирования) значения в регистре или в ячейке памяти.

Код

Команда

Код

Команда

04

INR B

05

DCR B

INR C

0D

DCR C

14

INR D

15

DCR D

INR E

1D

DCR E

24

INR H

25

DCR H

2C

INR L

2D

DCR L

34

INR [HL]

35

DCR [HL]

3C

INR A

3D

DCR A

Однобайтовые команды INR и DCR влияют на все флаги, кроме флага переноса.

Набор команд процессора 8080 также включает четыре команды циклического сдвига, которые сдвигают содержимое аккумулятора на один бит влево или вправо.

Код

Команда

Значение

07

RLC

Сдвинуть аккумулятор влево

0F

RRC

Сдвинуть аккумулятор вправо

17

RAL

Сдвинуть аккумулятор влево через бит переноса

1F

RAR

Сдвинуть аккумулятор вправо через бит переноса

Эти команды влияют только на флаг переноса.

Предположим, что аккумулятор содержит значение A7h, или 10100111 в двоичном формате. Команда RLC сдвигает биты влево. Старший бит, выталкиваемый за левую границу разрядной сетки, становится младшим, а также определяет состояние флага переноса. В результате получается значение 01001111, а флаг переноса устанавливается в 1. Команда RRC точно так же сдвигает биты вправо. После выполнения команды RRC значение 10100111 превращается в 11010011, а флаг переноса опять устанавливается в 1.

Команды RAL и RAR работают несколько иначе. При выполнении команды RAL содержимое аккумулятора сдвигается влево, старший бит сохраняется во флаге переноса, а в младший бит записывается предыдущее значение флага переноса. Например, если аккумулятор содержит значение 10100111, а флаг переноса равен 0, то после выполнения команды RAL содержимое аккумулятора меняется на 01001110, а во флаг переноса записывается 1. При тех же начальных условиях после выполнения команды RAR значение аккумулятора аналогично меняется на 01010011, а во флаге переноса сохраняется значение 1.

Команды сдвига удобны при умножении числа на 2 (сдвиг влево) и при делении числа на 2 (сдвиг вправо).

Память, к которой обращается микропроцессор, называется памятью с произвольным доступом потому, что микропроцессор может получить доступ к любой конкретной ячейке, просто предоставив ее адрес. Память RAM скорее напоминает книгу, которую можно открыть на любой странице, чем недельную подшивку газет на микрофильме. Чтобы найти нужную информацию в субботнем выпуске, мы должны просмотреть большую часть газет. Так, для воспроизведения последней песни на кассете мы должны практически полностью перемотать одну из ее сторон. Микрофильм и магнитная лента относятся к запоминающим устройствам не с произвольным, а с последовательным доступом.

Память с произвольным доступом, безусловно, хороша, особенно для микропроцессоров, но иногда удобнее использовать запоминающее устройство, доступ к которому осуществляется непроизвольно и непоследовательно. Допустим, вы работаете в офисе, и сотрудники подходят к вашему столу, чтобы дать задание. Выполнение каждого из них предполагает использование папки с документами. Часто при работе над одним заданием вы обнаруживаете, что не можете продолжать, пока не выполните определенную задачу, используя другую папку. Так что поверх первой папки вы кладете вторую и работаете с ней. Затем вам дают еще одно задание, более приоритетное, чем предыдущее, и вы кладете новую папку поверх двух других. Для выполнения этого вам требуется еще одна папка с документами. И вот на вашем столе уже целая стопка из четырех папок.

Это упорядоченный способ хранения и отслеживания всех выполняемых заданий. Самая верхняя папка всегда соответствует приоритетной задаче. После окончания работы с этой папкой вы переходите к следующей. Когда наконец вы разберетесь с последней папкой на своем столе (с той, с которой начали), сможете отправиться домой.

Технически такая форма хранения данных называется «стек». Строится он снизу вверх, а разбирается сверху вниз. Элементы стека организованы по принципу «последним вошел — первым вышел» (Last In First Out, LIFO). Последний элемент, помещенный в стек, удаляется из него первым. Первый добавленный в стек элемент будет удален из него последним.

Компьютеры также могут использовать стек, но не для хранения заданий, а для хранения чисел, что удобно. Добавление элемента в стек называется вталкиванием (push), а его удаление — выталкиванием (pop).

Предположим, вы пишете программу на языке ассемблера, в которой используются регистры A, B и C. На каком-то этапе программе требуется выполнить еще один небольшой расчет, также предполагающий применение регистров A, B и C. В итоге нужно вернуться к тому, что вы делали раньше, и продолжить использовать регистры A, B и C с теми значениями, которые в них хранились.

Безусловно, вы можете просто сохранить значения регистров A, B и C в других ячейках памяти, а затем загрузить их оттуда обратно. Однако тогда нужно будет следить за содержимым ячеек памяти. Более удобный способ — помещение (вталкивание) значений регистров в стек.

PUSH A

PUSH B

PUSH C

Я объясню, как работают эти команды, чуть позже. Пока достаточно понять, что они каким-то образом сохраняют содержимое регистров в памяти LIFO. После выполнения этих команд ваша программа может спокойно использовать эти регистры для других целей. Чтобы вернуть предыдущие значения, вы просто выталкиваете элементы из стека в обратном порядке.

POP C

POP B

POP A

Помните: последний помещенный в стек элемент удаляется из него в первую очередь. Случайное изменение порядка команд POP приведет к ошибке.

Преимущество стека в том, что его могут использовать разные разделы программы, не вызывая проблем. Например, после помещения в стек значений регистров A, B и C другому разделу программы может понадобиться сделать то же самое с регистрами C, D и E.