При адресации памяти для МП 8086 существует сложность другого рода, которую мы было «замели под ковер»: адрес, формируемый в любом из вышеуказанных режимов адресации, не является окончательным, действительным адресом, поскольку получаемый таким образом адрес имеет только 16 разрядов (и может адресовать только 64 Кбайт памяти). На самом деле то, что вычисляется по указанным выше правилам, называется смещением. Для того чтобы получить действительный, физический адрес, вы должны прибавить к смещению 20-разрядную базовую величину, получаемую сдвигом на 4 разряда влево содержимого 16-разрядного сегментного регистра (таких регистров имеется 4). Другими словами, МП 8086 позволяет вам обращаться к областям памяти по 64 Кбайт; положение этих «сегментов» внутри полного адресного пространства 1 Мбайт, что в свою очередь определяется содержимым сегментных регистров. Использование 16-разрядной адресации в МП 8086 по большому счету было большой ошибкой, унаследованной от ранних поколений.
Более современные МП (начиная с 80386, а также серии 68000) сделаны как надо во всех отношениях, с 32-разрядной адресацией. Для того чтобы не усложнять наши примеры, мы просто игнорируем сегменты вообще; на практике вы, конечно, должны будете о них побеспокоиться.
Обзор набора команд (продолжение). Рассмотрим теперь команды управления стеком PUSH и POP. Стек — это часть памяти, организованная специальным образом: когда вы заносите некоторое значение в стек (выполняя операцию push), это значение заносится в очередную доступную ячейку (вершину стека); а когда вы извлекаете значение из стека (выполняя операцию POP), оно выбирается из вершины стека, т. е. выбирается то, что было занесено в стек последним. Таким образом, стек — это последовательный набор данных, организованный по принципу: последним пришел-первым вышел. Вам, может быть, будет легче освоить это понятие, если вы представите себе монетную кассу водителя автобуса или стопку подносов в столовой.
Рис. 10.4 показывает, как работает стек. Стек располагается в обычном ОЗУ, а
Рис. 10.4.
Мы увидим, что при реализации вызовов подпрограмм и прерываний стек играет ведущую роль. Команда JMP заставляет ЦП отклониться от обычной процедуры последовательного выполнения команд, переходя к выполнению той команды, на которую совершается переход. Команда условного перехода (возможно 8 различных вариантов, обозначаемых обычно J
Она копирует 100 слов из массива, начинающегося с адреса 1000Н, в новый массив, начинающийся на 1 Кбайт (400Н), выше. Отметим явную загрузку указателя (в регистр ВХ, используемый для адресации) и счетчика цикла (в CL). Собственно массив слов должен быть пропущен через регистр (мы выбрали АХ), поскольку МП 8086 не поддерживает команды типа память-память (см. примечание к набору команд). В конце 100-го цикла CL = 0 и команда «перейти, если не нуль» (JNZ) более не выполняется. Этот пример будет работать[5], однако на практике вам, возможно, следует использовать более быстрые команды МП 8086-пересылки строк. Хорошим тоном в практическом программировании считается использование символьных имен для обозначения массивов и их размеров вместо соответствующих констант, таких как 400Н и 1000Н.
Оператор CALL является вызовом подпрограммы; он подобен каманде перехода, за исключением того, что адрес возврата (адрес команды, следующей за командой CALL) заносится в стек. В конце подпрограммы вы выполняете оператор RET, который извлекает из стека его содержимое так, чтобы программа могла найти «обратную дорогу» (рис. 10.5).
Рис. 10.5.
Три оператора STI, CLI и IRET имеют отношения к прерываниям, их работу мы проиллюстрируем вместе с примерами соответствующих электрических схем и ниже в этой главе. Наконец, команды ввода-вывода IN и OUT пересылают слово или байт между регистром А и соответствующим образом адресованным портом; подробнее об этом чуть позже.