Текст
                    СП.МОРС, ДДАЛБОТ
АРХИТЕКТУРА

МИКРОПРОЦЕССОРА
80286
Перевод с английского
В.Л. Григорьева
Москва
«Радио и связь»
1990


THE 80286 ARCHITECTURE Stephen P. Morse Douglas J. Albert A Wiley Press Book John Wiley & Sons, Inc. New York • Chichester • Brisbane • Toronto • Singapore
ББК 32.97 М80 УДК 681.325.5-181.4 Редакция переводной литературы Морс С. П., Алберт Д. Д. М 80 Архитектура микропроцессора 80286: Пер. с англ. - М.: Радио и связь, 1990. - 304 с: ил. ISBN 5-256-00466-2. В книге известных американских специалистов рассмотрена машинная организация и базовая система команд микропроцессора 80286. Показано проявление новых возможностей микропроцессора 80286 в режиме виртуального адреса, что в первую очередь касается аппаратной поддержки таких функций операционной системы, как сегментация памяти, защита, обработка прерываний, переключение с одной задачи на другую и т.п. Подробно рассмотрена регистровая модель и система команд арифметического сопроцессора 80287, а также особенности его взаимодействия с центральным процессором. Для инженеров-конструкторов микропроцессорных систем; М 2404040000-174 046(01}40 143-90 ББК 32.97 © 1986 by Stephen P. Morse and Douglas J. Albert Перевод на русский язык, предисловие к русскому изданию, примечания, список работ, переведенных на русский язык. Григорьев В.Л., 1990 ISBN 5-256-00466-2 (рус.) ISBN 0 471-83185-9 (англ.)
ПРЕДИСЛОВИЕ К РУССКОМУ ИЗДАНИЮ Микропроцессорная техника развивается чрезвычайно быстро. Кажется, совсем недавно советские инженеры-схемотехники осваивали 8-битный микропроцессор КР580ВМ80, а теперь он стал широко распространенным элементом множества разработок. В середине 80-х г.г. появился гораздо более мощный 16-битный микропроцессор К1810ВМ86; сейчас же он применяется довольно широко, например в профессиональных персональных компьютерах. Оба эти микропроцессора относятся к классу однокристальных микропроцессоров с фиксированными длиной слова и системой команд. За рубежом ведущие позиции в разработке и производстве однокристальных микропроцессоров, а точнее, функционально полных микропроцессорных семейств, занимает американская фирма Intel. Настоящая книга знакомит читателей с новым изделием этой фирмы - микропроцессором 80286. По цифровому коду этого микропроцессора нетрудно сделать вывод о том, что он продолжает линию микропроцессора 8086. Действительно, в одном из своих режимов работы (режима реального адреса) микропроцессор 80286 совместим с микропроцессором 8086 на уровне машинного языка. Но в нем введены и некоторые усовершенствования, например появились команды PUSHA и POP А, команда умножения допускает непосредственный операнд, расширены возможности команд сдвигов и т.д. Наличие режима реального адреса упрощает изучение микропроцессора 80286. Главные же архитектурные новинки этого микропроцессора проявляются при работе его в режиме виртуального адреса, ориентированного на аппаратную поддержку функций операционной системы. По существу, в режиме виртуального адреса микропроцессор 80286 архитектурно и функционально аналогичен процессорам крупных компьютеров 70-х гг. В частности, физический адрес памяти длиной 24 бита обеспечивает адресное пространство 16Мбайт. В "знак уважения" перед возможностями микросхемы 80286 в виртуальном режиме она далее называется процессором, а не микропроцессором. Впрочем, в американской литературе термины "процессор" и "микропроцессор" уже несколько лет употребляются как синонимы. 5
Первые две главы книги содержат обзорный материал, изложенный довольно кратко. В гл. 3 речь идет о системе команд процессора 80286, доступной в режиме реального адреса. Команды рассматриваются по функциональным группам и иллюстрируются примерами. Глава 4 посвящена арифметическому (математическому) сопроцессору 80287 и, очевидно, вызовет живой интерес у читателей. Эта удивительная по своим' возможностям микросхема (достаточно сказать, что ее внутренний формат чисел с плавающей точкой обеспечивает диапазон ± ю±5000 и точность 18 десятичных разрядов) расширяет сферу применения микропроцессоров на точные численные расчеты, требуемые в системах управления интеллектуальными роботами, графических терминалах, навигационных системах и т.п. Подробно рассмотрены регистровая модель сопроцессора, система команд и особенности взаимодействия с центральным процессором. Безусловно, наибольший интерес читателей вызовет гл. 5, в которой обсуждается режим виртуального адреса. В нее включен материал по управлению памятью, кольцам защиты, переключению с одной задачи на другую, обработке прерываний и особых случаев и др. В заключительной гл. 6 показано, как проектировать аппаратные средства систем на базе процессора 80286 с привлечением новых поддерживающих микросхем. Книга СП. Морса и Д.Д. Алберта будет полезна специалистам по вычислительной технике, разработчикам микропроцессорных систем и студентам старших курсов технических вузов. Изложение довольно сложных вопросов ведется в ней на высоком профессиональном уровне. Напомним в связи с этим, что С. П. Морс был одним из архитекторов семейства 8088/8086. Книга поднимает общий инженерный уровень специалистов по микропроцессорной технике на более высокую ступень. Им придется включить в свой обиход такие новые понятия, как дескрипторы и дескрипторные таблицы, атрибуты сегментов, шлюзы, конвейеризация шины и т.п. В заключение отметим, что для лучшего усвоения материала книги весьма желательно знакомство с двумя изданиями: Лю Ю-Чжен, Г. Гибсон "Микропроцессоры семейства 8086/8088. Архитектура, программирование и проектирование микрокомпьютерных систем" (М.: Радио и связь, 1987) и Г. Дейтел "Введение в операционные системы" (М.: Мир, 1987). В. Л. Григорьев б
Мегаиу и Аните посвящается ПРОЛОГ В НАЧАЛЕ... В самом начале фирма Intel разработала микропроцессоры 4004 и 8008, способные адресовать небольшую память и обладающие низкой производительностью. И фирма Intel сказала: "Да будет микропроцессор 8080!",- и вскоре появился этот микропроцессор с более широкими возможностями. Фирма Intel отделила рынок микропроцессора 8008 от рынка микропроцессора 8080. И фирма Intel сказала: "Да будет микропроцессор 8085 с внутренним генератором синхронизации, и пусть внутренний системный контроллер разделяет линии данных от управляющих линий!". И фирма Intel создала свод небесный и отделила добавленные команды, которые были под ним, от добавленных команд, которые были над сводом небесным. Первые команды фирма Intel назвала SIM (Set Interrupt Mask - установить маску прерываний) и RIM (Reset Interrupt Mask - сбросить маску прерываний), а вторые команды она никогда не объявляла. И фирма Intel сказала: "Да будет микропроцессор с внутренним постоянным запоминающим устройством для тех применений, для которых микропроцессор 8085 слишком могуч!". Новый микропроцессор назвали 8048, а областью его применений стал рынок процессоров с низкой производительностью. И фирма Intel сказала: "Да будет процессор нового поколения для рынка процессоров со средней производительностью. Пусть он оперирует 16-битными словами, адресует память 1Мбайт, имеет эффективные прерываемые цепочечные команды и полную десятичную арифметику!". И появился микропроцессор 8086, обладающий всеми этими возможностями. И фирма Intel сказала: "Да появится процессор для рынка процессоров с высокой производительностью и пусть он доминирует на этом рынке!". И появилась система iAPX 432 с отдельными процессором данных и процессором ввода-вывода. Она революционизировала микропроцессорный рынок, а по своим возможностям превосходила процессоры Z8000 и М68000 и всех других конкурентов. 7
И фирма Intel посмотрела на сделанное ею и сказала, что все хорошо. (Из журнала Computer, v. 13, № 10, p. 46, October 1980.) Я написал приведенный выше отрывок в конце 70-х гг., когда была опубликована моя первая книга "The 8086 Primer". Он показывает историю создания процессоров фирмы Intel от начала до конца 70-х гг. С того времени в мире процессоров семейства 8086 появилось много хороших разработок. Был создан 8-битный вариант микропроцессора 8086, названный микропроцессором 8088, который встроен в персональный компьютер фирмы IBM. Поэтому я переработал свою первую книгу и выпустил "The 8086/8088 Primer". Затем появился математический сопроцессор 8087, и я вместе с Д. Палмером опубликовал книгу "The 8087 Primer". После этого фирма Intel добавила несколько новых команд и ввела системные функции, в результате чего появился процессор 80186. (Его марку обычно сокращают до 186.) В новом процессоре 80286 добавлены механизмы управления памятью и защиты. В предлагаемой книге авторы попытались описать этого последнего представителя семейства процессоров 8086. Мы благодарны Р. Фарроу и Р. Шеллу за проверку рукописи и исправление многочисленных ошибок. Мы также благодарим Т. Тхурстона за проверку некоторых примеров на реальном процессоре 80286, Э. Макдо- нальда за советы по операционным системам и Ч. Брукса за помощь в изложении вопросов по аппаратным средствам. Выражаю особую признательность Д. Палмеру за разрешение использовать часть материала по сопроцессору 8087 из нашей совместной книги. Стефен П. Морс, д-р философии Сан-Франциско, шт. Калифорния, США
ПРЕДИСЛОВИЕ Эволюция микропроцессоров, без сомнений, оказывает огромное влияние на всю нашу жизнь. Первые микропроцессоры 4004 и 8008 обладали скромными вычислительными возможностями и применялись в научном приборостроении, простых системах управления, а также заменяли традиционную "жесткую" логику. Расширенные возможности микропроцессоров второго поколения 8080, Z80 и 8085 позволили применять их в микрокомпьютерах общего назначения и в учрежденческой деятельности, в основном, для обработки текстов. С появлением операционной системы СР/М стало возможным широко использовать микропроцессоры в микрокомпьютерах общего назначения. Появление семейства микропроцессоров 8086/8088 дало сильнейший толчок автоматизации управленческого труда. Персональный компьютер IBM PC с микропроцессором 8088 позволил сразу же значительно повысить производительность труда. Простой и быстрый перенос программного обеспечения системы СР/М-80 обеспечил широкое использование готовых прикладных программ. Один из авторов настоящей книги С. П. Морс входил в группу разработчиков семейства 8086/8088. В этом семействе микропроцессоров особенно важную роль сыграли поддержка языков высокого уровня и сегментная адресация. Следующим этапом в эволюции микропроцессоров стало семейство iAPX 286. Для него характерны мощные функции поддержки операционной системы, дополняющие средства языков высокого уровня предыдущего семейства. Благодаря iAPX 286 возможности крупных компьютеров за приемлемую стоимость оказываются в распоряжении работников различных учреждений. Настоящая книга знакомит читателей с возможностями и применением семейства iAPX 286. Материал построен по привципу "от простого к сложному". Рассмотрение вопросов системной организации сопровождается практическими примерами. Авторам удалось изложить цели, практическую направленность и простоту всех тех механизмов, которые реализованы при разработке семейства iAPX 286. Р. Чайлдс, архитектор семейства iAPX 286 9
ГЛАВА 1 ВВЕДЕНИЕ Микросхема 80286 — один из представителей все расширяющегося семейства микропроцессоров. Во время подготовки настоящей книги ведется интенсивная работа над новейшим процессором 80386 . Чтобы лучше разобраться в современном поколении микропроцессоров, нужно оглянуться назад и посмотреть, с чего все началось и как шло их развитие. В этой главе и дается такая историческая ретроспектива, а также определяются основные понятия компьютеров и внутреннее представление чисел. Остальные главы книги построены следующим образом. В гл. 2 и 3 представлена базовая архитектура процессора 80286. Так как она аналогична архитектуре микропроцессора 8086, читатели, знающие микропроцессор 8086, могут пропустить этот материал. Глава 4 посвящена архитектуре численного процессора 80287. В основном она похожа на архитектуру микропроцессора 8087, и знакомые с ней читатели могут пропустить гл. 4. Наибольшие различия между микропроцессорами 8086 и 80286 касаются той поддержки, какую процессор 80286 оказывает операционной системе. Об этом речь идет в гл. 5. Наконец, в гл. 6 затронуты аппаратные аспекты разработки законченного компьютера с процессорами 80286 и 80287. 1.1. ЭВОЛЮЦИЯ МИКРОКОМПЬЮТЕРОВ Процесс эволюции микроэлектроники, который привел к созданию современных микрокомпьютеров, можно разделить на два периода: уменьшения размеров и расширения возможностей. В течение первого периода (40 - 70-е гг.) по мере совершенствования технологии компонентов компьютеры становились все меньше и меньше. Кульминацией этого периода стало появление компьютера (хотя и примитивного по стандартам 1970 г.), размеры которого были не больше почтовой марки. В период расширения возможностей (с 1970 г. по настоящее время) крошечные компьютеры превратились в столь же мощные образования как и их крупные предшественники. Уменьшение размеров. В 50-х гг. все электронные устройства, от радиоприемников и телевизоров до компьютеров, были построены на громоздких электронных лампах. Компьютеры этого периода иногда называют ЭВМ первого поколения, а примерами их служат модели 650 и 704 Выпускается фирмой Intel с 1986 г. -Прим. перев. 10
фирмы IBM. Эти компьютеры устанавливались в больших помещениях и состояли из нескольких стоек с электронным оборудованием. К концу 50-х гг. электронные лампы начали заменять транзисторами и другими твердотельными приборами. Компьютеры, выполненные по новой технологии, стали называть ЭВМ второго поколения (примерами их служат системы машины 7090 фирмы IBM и В5500 фирмы Burroughs). В 60-х гг. дискретные электронные эйементы (транзисторы, резисторы и др.) были объединены в более сложные электронные компоненты, названные интегральными схемами. Интегральная схема изготавливается на кремниевой пластинке, размеры которой меньше размеров почтовой марки. Пластинка монтируется в корпусе со многими выводами ("сороконожка"), который можно встроить в систему. Такая интегральная схема называется чипом (кристаллом). Компьютеры, построенные на интегральных схемах, относятся к ЭВМ третьего поколения (системы IBM 360, GE 635 и Burroughs 6700). Технология интегральных схем продолжала совершенствоваться, и в начале 70-х гг. многие компоненты ЭВМ удалось разместить в одной микросхеме (микропроцессоры 4004 и 8008 фирмы Intel). Появился термин компьютер на кристалле. Компьютеры на кристалле называются микрокомпьютерами и микропроцессорами. Хотя этим терминам иногда придается одинаковый смысл, между ними имеется различие. Микропроцессор - это одна микросхема, содержащая схемы управления и арифметические устройства компьютера, но в ней нет памяти и устройств ввода-вывода. Микрокомпьютер - это законченная система, содержащая микропроцессор, микросхемы памяти и устройства ввода-вывода. Иногда вся система реализуется на одном кристалле (микросхема 8048 фирмы Intel), и тогда получается однокристальный микрокомпьютер. Расширение возможностей. Эра микропроцессоров началась в 1971 г. с появлением микросхем 4004 и 8008 фирмы Intel. Они относятся к микропроцессорам первого поколения. Обе эти микросхемы разрабатывались для специализированных применений: 4004 - для калькулятора, а 8008 - для терминала. Оба микропроцессора считались в то время занимательной новинкой и всерьез не воспринимались. Но к 1974 г., когда микропроцессор 8008 был модифицирован в микропроцессор второго поколения 8080, на них обратила внимание компьютерная промышленность. Микросхема 8080 была первым микропроцессором, специально разработанным для множества применений; она быстро стала "стандартным" микропроцессором. Теперь микропроцессор стал выполнять вычислительные задачи старых и громоздких компьютеров и оказался по стоимости доступным даже для любителей. Многие фирмы выпускали микропроцессор 8080 по лицензиям, а некоторые из них предложили улучшенный его вариант и
(микропроцессор Z80 фирмы Zilog). В 1976 г. появился модернизированный вариант микропроцессора 8080 - микросхема 8085 фирмы Intel. Однак^ до 1978 г. базовая структура микропроцессора 8080 оставалась неизменной. В 1978 г. фирма Intel выпустила микропроцессор 8086. Это был первый микропроцессор, который работал с данными длиной 16 бит (микропроцессор 8080 работает с 8-битными данными). Вскоре еще две фирмы объявили о своих 16-битных микропроцессорах: фирма Motorola выпустила микропроцессор М68000, а фирма Zilog - микропроцессор Z8000. Все эти микропроцессоры образовали третье поколение микропроцессоров. В 1979 г. был выпущен 8-битный вариант 8086 - микропроцессор 8088. Через два года фирма IBM вышла на рынок персональных компьютеров и использовала его в своем первом изделии - персональном компьютере IBM PC. Благодаря такой мощной поддержке микропроцессоры 8086 и 8088 стали наиболее популярными. Семейство микропроцессоров 8086 продолжало развиваться. В 1983 г. фирма Intel разработала усовершенствованные модификации - процессоры 80186 и 80188. В этом же году был объявлен процессор 80286, который стал крупным шагом вперед по сравнению с микропроцессором 8086. Через год этот процессор был встроен в персональный компьютер фирмы IBM следующего поколения - IBM PC/AT. Сделаем замечание по названиям процессоров. Фирма Intel называет процессоры - iAPX 186. iAPX 188 и iAPX286. Однако вне ее их назьюают - 80186, 80188 и 80286; этого же будем придерживаться и мы, хотя имеют хождение и сокращенные названия процессоров - 186, 188 и 286. Параллельно с расширением сферы применения микропроцессоров развивались и специализированные микропроцессоры. Под сопроцессором понимается подчиненный процессор, выполняющий специализированные функции для процессора широкого назначения. Первым популярным сопроцессором оказался 8087, который выполняет вычисления с плавающей точкой для микропроцессоров 8086 и 8088. С появлением процессоров 80186 и 80188 он стал применяться и с ними. Но поскольку в процессор 80286 встроен другой интерфейс с сопроцессором, для него потребовалось модифицировать сопроцессор 8087. Таким усовершенствованным сопроцессором стала микросхема 80287 (другие названия ее 287 и iAPX287). 1.2. ОСНОВНЫЕ СВЕДЕНИЯ О КОМПЬЮТЕРАХ Предполагается, что читатели знакомы с принципами построения компьютеров, поэтому мы дадим здесь только краткий обзор. Компьютер получает данные от устройств ввода, обрабатывает их и передает окончательные результаты в устройство вывода. Выполняемая обработка определяется последовательностью команд (инструкций), называемой программой. Программа хранится в памяти компьютера. 12
\ Операциями компьютера управляет центральный процессор, или прЬсто процессор. Он выбирает команды из памяти, декодирует их и выполняет операции, предписанные командами. Чтобы выполнять операции^ процессор должен посылать управляющие сигналы в другие устройства компьютера. Операции, производимые ими при выполнении команды, состоят из пересылок данных и вычислений. Память компьютера хранит исходные данные для вычислений и результаты. Чтобы показать, как работает компьютер, рассмотрим выполнение команды сложения. Процессор посылает сигнал в память, запрашивая следующую команду, а память реагирует, передавая команду в процессор. Затем процессор декодирует команду и обнаруживает, что это команда сложения. После этого процессор предпринимает такие действия: 1) посылает сигналы в память, запрашивая передачу двух значений; 2) суммирует полученные значения; 3) посылает сигнал в память о том, чтобы она приняла результат сложения. Память - это совокупность последовательных ячеек, каждая из которых имеет уникальный адрес. Каждая ячейка состоит из последовательности бит. Значения бит (0 или 1) образуют содержимое ячейки. Регистры, как и память, используются для хранения промежуточных результатов. Но они находятся в составе процессора, поэтому получать значения из регистров проще и быстрее, чем из памяти. Флажки внутри процессора применяются для регистрации того, что в нем происходит. Есть два вида флажков: одни из них (флажки состояния) фиксируют информацию об особенностях ранее выполненных команд, а другие (флажки управления) управляют действиями процессора. Пример флажка состояния - флажок, показывающий, не является ли результат для компьютера слишком большим. Примером флажка управления служит флажок, заставляющий компьютер выполнять команды с меньшей скоростью. Может оказаться, что флажок одновременно является и флажком состояния, и флажком управления; примером может служить флажок NT процессора 80286. 1.3. ПРЕДСТАВЛЕНИЕ ЧИСЕЛ Мы привыкли представлять целые числа в виде последовательностей десятичных цифр, например 365: три сотни, шесть десятков и пять единиц. Такое представление называется представлением с основанием десять. Целые числа в компьютерах обычно представляются последовательностями двоичных цифр (бит), например 11010. Такое представление в двоичной системе обозначает: 1 - шестнадцать, 1 - восемь, 0 - четыре, 1 - два и 0 - нуль. Двоичные числа можно складывать, вычитать, умножать и делить, не 13
превращая их в десятичные, если помнить о том, что 1 плюс 1 равно 10 0- два и 0 - нуль), а не 2. Приведем пример: 1001 (двоичное представление девяти) 0101 (двоичное представление пяти) 1110 (двоичное представление четырнадцати) Конечно, мы запутаемся в длинных последовательностях двоичных цифр, но компьютеры в них не путаются. Например, 10110101 есть двоичное представление десятичного числа 181. Чтобы упростить действия с двоичными числами, их цифры группируют по четыре бита. После этого каждая группа представляется одним символом в соответствии с табл. 1.1. Например, число 10110101 сокращенно записывается как В5. Такое представление называется шестнадцатеричным числом; если бы мы рождались с шестнадцатью пальцами на руках, то пользовались бы именно такими числами. Таблица 1.1. Шестнадцатеричное представление Группа из 4 бит Шестнадцатеричная цифра Значение 0000 0 нуль 0001 1 один 0010 2 два ООН 3 три 0100 4 четыре 0101 5 пять оно б шесть 0111 7 семь 1000 8 восемь 1001 9 девять 1010 А десять 1011 В одиннадцать 1100 с двенадцать 1101 D тринадцать 1110 Б четырнадцать 1111 F пятнадцать Двоичная запись очень удобна для представления положительных чисел и нуля. Но при переходе к отрицательным числам потребуется дополнительный механизм для указания знака числа. Проще всего использовать для знака старший (левый) бит числа, например: 14
0000 0100 (обозначает +4) 1000 0100 (обозначает - 4) 01111111 (обозначает+127) 11111111 (обозначает-127) ■У такого представления, называемого прямым кодом, имеется один серьезный недостаток: для него потребуются специальные арифметические правила. Покажем это на примере использования двоичной арифметики для вычитания +1 из 0 с ожидаемым получением -1: _0000 0000 (0 в прямом коде) ~0000 0001 (+1 в прямом коде) 11111111 (-127 в прямом коде) При использовании для знаковых чисел (как и для беззнаковых) обычной двоичной арифметики требуется особое представление знаковых чисел, в котором 11111111 представляет -1, а не -127. Кроме того, вычитание +1 из -1 должно давать -2. Выполним это вычитание, чтобы посмотреть, как должно выглядеть —2: _11111111 (это-1) ~0000 0001 (вычитаем +1) 11111110 (и называем это -2) Рассмотренное представление называется дополнительным кодом; в этом коде операции сложения и вычитания дают правильный результат в дополнительном коде, например: +0000 0011 (+3 в дополнительном коде) 11111110 (- 2 в дополнительном коде) 0000 0001 (+1 в дополнительном коде) В дополнительном коде старший бит неотрицательного (положительного или нулевого) числа содержит 0, а отрицательного числа -1. Следовательно, как и в прямом коде, этот бит является знаковым. Знак числа в дополнительном коде можно изменить, если изменить (инвертировать) значение каждого бита и прибавить +1. Например, мы можем получить представление -5 в дополнительном коде из представления +5 в дополнительном коде следующим образом: 0000 0101 (+5 в дополнительном коде) 11111010 (+5 с измененными битами) 0000 0001 (+1 в дополнительном коде) 11111011 (-5 в дополнительном коде) Необходимо очень осторожно подходить к увеличению длины чисел, представленных в дополнительном коде. Бели 8-битное число в дополни- 15
тельном коде расширяется до 16 бит (например, для сложения с 16-битным числом в дополнительном коде), нужно подумать, что же поместил, в левые 8 бит. / Предположим, что мы хотим прибавить число 0000 0001 (+1 в дополнительном коде) к 0000 0000 0000 ООН (+3 в дополнительном коде). Наверное, ни у кого не возникает сомнений в том, что здесь следует просто добавить в числе +1 восемь нулей с левой стороны, а затем сложить: 0000 0000 0000 0011 (+3 в дополнительном коде) *0000 0000 0000 0001 (+1 в дополнительном коде) 0000 0000 0000 0100 (+4 в дополнительном коде) Однако при необходимости прибавить число 1111 1111 (-1 в дополнительном коде) к числу 0000 0000 0000 ООН (+3 в дополнительном коде) следует добавить к числу -1 слева восемь единиц (добавление нулей превратило бы -1 в положительное число). После этого производится сложение: 0000 0000 0000 0011 (+3 в дополнительном коде) 1111111111111111 (-1 в дополнительном коде) 0000 0000 0000 0010 (+2 в дополнительном коде) Следовательно, расширение 8-битного числа в 16-битное выглядит так: Значение 8-битное представление 16-битное представление +1 0000 0001 0000 0000 0000 0001 -1 1111 1111 11111111 11111111 Таким образом, для расширения числа в дополнительном коде необходимо ввести новые биты слева и поместить в каждый из них значение знакового бита. Этот процесс называется расширением со знаком. ЗАКЛЮЧЕНИЕ Познакомившись с компьютерами и представлением чисел, мы готовы к изучению конкретного процессора 80286. В следующей главе рассматривается его машинная организация. 16
\ ^ ГЛАВА 2 ^ МАШИННАЯ ОРГАНИЗАЦИЯ ПРОЦЕССОРА 80286 '< 2.1. ВВЕДЕНИЕ Один из способов изучения компьютера заключается в рассмотрении его функциональных компонентов. Описание этих компонентов и их взаимодействия иногда называется архитектурой компьютера. В понятие архитектуры входят число регистров и их функции, объем подключаемой памяти, способы ее адресации и средства ввода-вывода. Микросхема 80286 содержит значительную часть компонентов компьютера, например схемы, которые управляют всеми его функциями, а также все регистры и флажки. В ней нет памяти и устройств ввода-вывода, но их легко подключить к микросхеме и образовать законченный компьютер. Совокупность всех элементов, содержащихся на кристалле микросхемы, иногда называют процессором. Процессор 80286 имеет два режима работы: режим реального адреса (или, короче, реальный режим) и защищенный режим виртуального адреса (виртуальный режим). В первом режиме процессор ведет себя как более быстродействующий микропроцессор 8086, и многие программисты будут использовать его именно в этом режиме. Виртуальный режим предназначен для системных программистов и пользователей, разрабатывающих сверхбольшие программы. В настоящей главе мы сосредоточимся на реальном режиме, а виртуальный режим подробно рассмотрим в гл. 5. Процессор 80286 имеет четыре набора регистров: регистры общего назначения для хранения промежуточных результатов; указательные и индексные регистры для локализации информации в определенных областях памяти; сегментные регистры, которые служат для задания этих областей памяти; в последний набор входит указатель команды. Кроме того, в процессоре находится девять флажков, фиксирующих текущее состояние и управляющих его работой. Процессор может обращаться более чем к 1 млн. байт памяти (и намного больше в виртуальном режиме) и более чем к 65 000 входных и выходных портов. Мы обсудим эти возможности в следующих трех разделах. Типичные команды компьютера локализуют операнды (т.е. обрабатываемые данные), выполняют операцию над значениями операндов и помещают результат в указанное место. В зависимости от команды операнды и результат могут находиться в памяти или регистрах. Средства их локализации называются режимами адресации операндов; они рассматриваются во второй половине настоящей главы. Собственно командам посвящены гл. 3 и 5. 17
2Л. СТРУКТУР А ПАМЯТИ / Память системы, работающей в реальном режиме, образуют 220 /примерно 1 млн.) 8-битных величин, называемых байтами. Каждому байту назначен уникальный адрес (беззнаковое число) из диапазона от 0 до Q20 - - 1 (от 00000 до FFFFF в шестнадцатеричной системе счисления), что показано на рис. 2.1. В виртуальном режиме размер памяти расширяется до 224 (16 млн.) байт. Любые два смежных (или соседних) байта в памяти образуют слово. У каждого из двух байт в слове есть свой адрес и меньший из них принимается за адрес слова. Примеры слов показаны на рис. 2.2. Рис. 2.1. Адреса памяти Шестнадцате- ричный адрес 00000 00001 00002 00003 Двоичный адрес 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0010 0000 0000 0000 0000 0011 Память FFFFE FFFFF 1111 1111 1111 1111 1110 1111 1111 1111 1111 1111 Адрес байта (шестнадцате- ричный) 00000 00001 00002 00003 00004 00005 00006 00007 00008 00009 0000А 0000В Память ххххххх } } } } Слово, начинающееся по четному адресу Слово, начинающееся по нечетному адресу Два перекрывающихся слова Рис. 2.2. Примеры слов в памяти 18
Увеличение адресов 0 С F 7 3 В 2 F Младший байт Старший байт Младший байт Старший байт Содержимое слова равно F70C Содержимое слова равно 2F3B Рис. 2.3. Примеры "обратного" хранения в памяти Слово состоит из 16 бит. Байт с большим адресом содержит старшие биты слова, а байт с меньшим адресом - младшие. Сначала такая ситуация кажется вполне естественной: конечно же, старший байт должен иметь больший адрес. Но когда память рассматривается как последовательность байт, простирающаяся от наименьшего адреса к наибольшему, оказывается, что процессор хранит слова "наоборот", что показано на рис. 2.3. 2.3. СЕГМЕНТАЦИЯ ПАМЯТИ Поскольку процессор 80286 может адресовать до 220 байт (и намного больше в виртуальном режиме), можно было бы ожидать, что внутри него адреса байт и слов должны быть представлены 20-битными величинами. Однако процессор рассчитан на 16-битную арифметику, поэтому длина адресных объектов ограничена 16 битами. Следовательно, для построения адресов необходим какой-то дополнительный механизм. Можно полагать, что в реальном режиме память 1М байт состоит из нескольких сегментов, каждый из которых содержит самое большее 216 (64К) байт. Сегменты начинаются по адресам, кратным 16, т.е. имеющим четыре нулевых младших бита. В любой момент времени программа может обращаться к четырем сегментам, которые называются текущим сегментом кода, текущим сегментом данных, текущим сегментом стека и текущим дополнительным сегментом (он обычно отводится для данных). Каждый текущий сегмент идентифицируется путем размещения старших 16 бит адреса его первого байта в одном из четырех специальных сегментных регистров. Отметим, что сегменты могут перекрываться, как видно на рис. 2.4. Предположим, например, что в 16-битном сегментном регистре кода содержится число СО 18. Это значит, что сегмент кода начинается по адресу байта С0180 и простирается на 216 (100001б) байт. Следовательно, последний байт сегмента кода имеет адрес D017F. 19
Сегментный 1 регистр коде ] Сегментный i регистр данных J Сегментный 1 регистр стека J Т6 бит г [оооо—I 16 бит 10000- 16 бит 10000- Сегментныи «- регистр допол-1 нительных*" данных 16 бит J0000 /////✓// Сегмент кода ' //////// /Допол^ 'нитель- 'ный сег7 ''мент'' Сегмент it / / стека КХХХХХХ» Сегмент данных 2 и байт 2» байт 2i6 байт -L Т 2i6 байт Рис 2.4. Примеры сегментов (здесь сегменты стека и данных перекрываются) | 1 байтный сегментный регистр ' |о ООО J*^''**"M^164Sht^ с 20-битный адрес байта или слова Рис 2.5. Формирование адресов байта или слова Память Сегментный регистр кода CS I С 0 1 8 т FE7F _L -С0180 CFFFF 0017F Сегмент кода Рис 2.6. Пример формирования адреса байта 20
Мы обращаемся к байтам или словам внутри сегмента с помощью 16-битного внутрисегментного смещения. Процессор образует 20-битный адрес байта или слова, суммируя 16-битное смещение с содержимым 16-битного сегментного регистра, к которому "пристроены" четыре младших нуля (рис. 2.5.). В предыдущем примере байт с адресом CFFFF находится внутри текущего сегмента кода, т.е. он имеет смещение FE7F (CFFFF - С0180) в сегменте, как показано на рис 2.6. В виртуальном режиме сегменты и смещения сохраняются. Различие заключается в том, что начальные адреса сегментов не образуются путем добавления четырех нулей к содержимому сегментных регистров, а берутся из таблиц, индексируемых сегментными регистрами (см. гл. 5). 2.4. СТРУКТУРА ВВОДА-ВЫВОДА Система на базе процессора 80286 взаимодействует с внешним миром через так называемые порты. Через них процессор может получать информацию о внешних событиях и выдать сигналы, управляющие другими событиями. Процессор может обращаться к 216 (64К) 8-битным портам примерно так же, как к байтам памяти. Каждому 8-битному порту назначен уникальный адрес из диапазона от 0 до 216 - 1. Любые два смежных 8-битных порта можно считать 16-битным портом аналогично слову памяти. 2.5. РЕГИСТРЫ Процессор имеет 13 16-битных регистров и 9 однобитных флажков (флажки NT и IOPL предназначены только для виртуального режима). Мы разделим регистры на четыре набора: три набора содержат по четыре регистра, а непосредственно недоступный программисту указатель команды образует отдельный набор. Регистры и флажки показаны на рис. 2.7. Регистры общего назначения служат, в основном, для хранения операндов арифметических и логических операций; указательные и индексные регистры предназначены для хранения внутрисегментных смещений, а сегментные регистры определяют начальные адреса сегментов. Регистры общего назначения. В процессоре, не имеющем регистров общего назначения (РОН), каждая команда должна считывать свои операнды из памяти и возвращать результат также в память. Однако на обращение к памяти расходуется время, которое можно уменьшить, если временно хранить часто используемые операнды и результаты в более быстродос- тупном месте. Таким местом и являются регистры общего назначения. РОНы в процессоре 80286 представлены 16-битными регистрами АХ, ВХ, СХ и DX. Младшую и старшую половины каждого РОНа можно использовать либо отдельно (как два 8-битных регистра), либо совместно (как один 21
Регистры общего назначения О 7 О АХ АН AL Аккумулятор 8Х ВН BL База :х СН CL Счетчик ох DH DL Данные 15 Указательные и индексные регистры q SP BP SI Di Указатель стека Указатель базы Индекс источника Индекс приемника 15 Сегментные регистры CS DS SS ES Код Данные Стек Дополнительные данные «5 Указатель команды и флажки q IP FLAGS NT - IOPL Указатель команды Рис. 2.7. Регистры и флажки процессора 80286 16-битный регистр). В связи с этим каждой половине РОНа дано свое название: младшие (Low) половины называются AL, BL, CL и DL, а старшие (High) - АН, ВН, СН и DH. Двойственный характер РОНов позволяет одинаково легко оперировать байтами и словами. Большей частью содержимое РОНов единообразно участвует в арифметических и логических операциях. Например, по команде ADD (сложить) можно прибавить содержимое любого 8- или 16-битного РОНа к содержимому любого другого РОНа такого же размера и сохранить результат в любом 22
из них. Однако в некоторых командах функции РОНов специализированы. Например, в цепочечных командах регистр СХ должен хранить число элементов цепочки; для этого нельзя привлекать регистры АХ, ВХ и DX. Такая специализация предопределяет описательное название регистра СХ - СЧЕТЧИК. Специальные функции регистров АХ, ВХ и DX (см. далее) объясняют их названия: АККУМУЛЯТОР, БАЗА и ДАННЫЕ. Специализация РОНов затрудняет изучение процессора из-за необходимости помнить специальные правила. Она же несколько увеличивает длину программ, так как до выполнения некоторых команд приходится пересылать данные из одного РОНа в другой. Однако рассмотрим разработку программы для процессора, в котором все РОНы одинаковы. Чтобы следить за ее выполнением, нам, вероятно, придется организовать программу так, чтобы определенные данные всегда находились в конкретных регистрах. Например, можно договориться всегда использовать регистр СХ для учета числа элементов цепочки. Тогда нам никогда не придется пересылать размер цепочки в регистр СХ, так как он всегда будет там. Но поскольку цепочечная команда в нашем гипотетическом процессоре может получать размер цепочки из любого РОНа, каждая цепочечная команда должна указывать, где находится размер цепочки. Для этого придется лцбо увеличить длину каждой цепочечной команды (два байта вместо одного), либо ввести больше однобайтных цепочечных команд. Лервое решение прямо ведет к увеличению программы, а второе имеет такие же последствия. Действительно, всего может быть только 256 однобайтных команд, и увеличение числа однобайтных цепочечных команд заставит удлинить другие одаобайтные команды до двух байт. Таким образом, специализация РОНов в некоторых командах уменьшает длину программы. Указательные и индексные регистры. Команда с обращением к ячейке памяти может прямо указать адрес этой ячейки, но прямой адрес увеличивает длину команды и размер программы (кода). Если адреса часто используемых ячеек хранить в специальных регистрах, то в команде адрес можно не указывать, а определить регистр, в котором находится адрес. Такие регистры иногда называются указательными или индексными. Данное использование регистров напоминает сокращенный набор номеров телефонов. Вы можете позвонить любому человеку в городе, набирая семизначный номер его телефона. Но, если телефонная компания предлагает такой сервис, можно поместить часто используемые номера абонентов в "регистры". После этого для соединения с нужным абонентом набираются одна или две цифры, определяющие регистр. К указательным и индексным регистрам процессора 80286 относятся 16-битные регистры SP, BP, SI и DI, которые обычно содержат внутрисегментные смещения. Например, в команде ADD один из операндов может 23
находиться в текущем сегменте данных со смещением, содержащимся в одном из указательных или индексных регистров, скажем SI. Помимо сокращения длины команд рассматриваемые регистры выполняют еще одну (возможно, более важную функцию): они позволяют командам обращаться к ячейкам, смещения которых являются результатом предыдущих вычислений в ходе выполнения программы. Такие вычисления часто требуются (особенно в программах на языках высокого уровня) для того, чтобы установить смещения переменных. Требуемые вычисления можно осуществить в РОНах, а затем переслать результат в указательный или индексный регистр для использования его как смещения. Если устранить подобные пересылки, программы будут короче. Поэтому содержимое указательных и индексных регистров может участвовать в арифметических и логических операциях наряду с 16-битными РОНами. Например, в упомянутой выше команде ADD вторым операндом может быть содержимое регистра SI. Рассматриваемые регистры имеют некоторые различия, что и объясняет их разделение на указательные и индексные. Указательные регистры SI и DI предназначены для удобного доступа к данным в текущем сегменте данных, а базовые регистры SP и BP - в текущем сегменте стека. Использование сегмента стека как "области данных" дает некоторое преимущество в реализации языков высокого уровня (см. далее). Поэтому, если сегмент явно не указан, смещения в базовых регистрах относятся к текущему сегменту стека, а смещения в индексных регистрах обычно относятся к сегменту данных. (Слово "обычно" подразумевает исключения, о которых речь пойдет далее.) Если, например, в команде ADD смещение одного из операндов находится в регистре SI, то считается, что операнд содержится в сегменте данных, но можно явно определить и другой сегмент. В некоторых командах учитывается различие между двумя базовыми регистрами SP и BP. Например, в командах PUSH и POP смещение верхнего элемента стека берется из регистра SP, что объясняет его название УКАЗАТЕЛЬ СТЕКА. Использовать для этой цели регистр BP нельзя. Этот регистр содержит смещение "базы" области данных в сегменте стека, поэтому и называется УКАЗАТЕЛЕМ БАЗЫ. Кроме того, в цепочечных командах существует различие между индексными регистрами SI и DI. Предполагается, что смещение операнда-источника содержится в регистре SI, а операнда-приемника - в регистре DI. Этим объясняется название этих регистров ИНДЕКС ИСТОЧНИКА и ИНДЕКС ПРИЕМНИКА. Функции регистров SI и DI в цепочечных командах изменить нельзя. Например, команда пересылки цепочки передает цепочку из текущего сегмента данных со смещением из регистра SI в текущий дополнительный сегмент со смещением из регистра DI; регистры SI и DI в команде явно не указываются. (Цепочка-приемник находится в дополни- 24
тельном сегменте, а не в сегменте данных, поэтому у каждой цепочки есть свой сегмент и длина ее может доходить до 216 байт.) Сегментные регистры. Напомним, что в реальном режиме память состоит из Ш байт, но адреса, содержащиеся в командах и в указательных и индексных регистрах, имеют длину всего 16 бит. Они не могут адресовать всю память, а являются смещениями в каком-то конкретном сегменте емкостью 64 Кбайт. Но в каком же? В процессоре 80286 для задания текущих сегментов предназначены четыре 16-битных сегментных регистра CS, DS, SS и ES. Каждый регистр идентифицирует конкретный текущий сегмент и функции их совершенно различны: CS идентифицирует текущий сегмент кода, DS - текущий сегмент данных, SS - текущий сегмент стека и ES - текущий дополнительный сегмент (данных). Таким образом, команда задает смещение в сегменте, а сегментные регистры определяют для нас четыре нужных. Какой же следует выбирать? Ответ зависит от того, как команда использует смещение, которое может определять либо следующую выполняемую команду, либо операнд команды. Выборки всех команд осуществляются из текущего сегмента кода, поэтому нужен регистр, содержащий смещение следующей выполняемой команды в текущем сегменте кода. Эту функцию выполняет регистр IP - УКАЗАТЕЛЬ КОМАНДЫ. Если, например, регистр CS содержит 1FF7, а регистр IP - 003А, то следующая выполняемая команда выбирается из ячейки IFF А А: 1FF70 (начальный адрес сегмента кода) 003А (смещение в регистре IP) 1FF А А (адрес следующей команды) Напомним, что для образования адреса памяти к содержимому регистра CS "пристраивается" шестнадцатеричная цифра 0 (см. рис. 2.5). Сегмент, из которого считывается операнд, в общем случае можно указать, предпослав команде специальный однобайтный префикс. Он определяет, из какого текущего сегмента считывается операнд. При отсутствии префикса (а это обычная ситуация) операнд берется из текущего сегмента данных, но здесь имеются исключения: если в вычислении смещения участвует указательный регистр, то привлекается текущий сегмент стека; в случае операнда-приемника цепочечной команды используется дополнительный сегмент. Указание префикса в цепочечной команде может повлиять только на операнд-источник; операнд-приемник всегда находится в текущем дополнительном сегменте. 25
Рассмотрим команду ADD; один из операндов которой находится в сегменте данных, а смещение содержится в регистре SI. Команда должна указать регистр SI в своем поле операнда, но может не определять регистр DS. При выполнении команды процессор знает, что для локализации операнда необходимо использовать содержимое регистров DS и SL Теперь обратимся к команде ADD, один из операндов которой находится в сегменте кода (им, например, может быть константа), а смещение содержится в регистре SL Команда ADD, как и раньше, должна определить регистр SI в поле операнда; при выполнении же команды ей должен предшествовать байт префикса, идентифицирующий регистр CS. Флажки. Процессор 80286 имеет 9 флажков, которые применяются для регистрации состояния (флажки состояния) и управления действиями процессора (флажки управления). Флажки состояния обычно устанавливаются после выполнения арифметических и логических команд, отражая определенные свойства их результатов. К ним относятся: флажок переноса CF, показывающий перенос из старшего бита; флажок вспомогательного переноса AF, фиксирующий перенос из четырех младших бит; флажок переполнения OF, определяющий выход знакового результата за границы диапазона; флажок нуля ZF, показывающий нулевой результат команды; флажок знака SF, регистрирующий отрицательный результат; флажок паритета (четности) PF, фиксирующий наличие в результате четного числа единичных бит. К флажкам управления относятся флажок направления DF (показывает направление прохождения цепочек в цепочечных командах), флажок разрешения прерываний IF (разрешает или запрещает восприятие прерываний по входу INTR) и флажок трассировки TF (переводит процессор в пошаговый режим для отладки программы). Подробнее о функциях флажков мы поговорим в гл. 3 и 5. 2.6. ОПЕРАНДЫ И РЕЖИМЫ АДРЕСАЦИИ ОПЕРАНДОВ Команды обычно выполняют операции над одним или двумя операндами. Например, команда ADD прибавляет значение одного операнда к значению второго операнда и запоминает результат в одном из них. Команда инкремента INC прибавляет 1 к значению единственного операнда и сохраняет результат на месте операнда. Теперь нам нужно детально рассмотреть, как команда определяет свои операнды, т.е. режимы адресации операндов. Один операнд» Обратимся к команде, которая определяет единственный операнд, например к команде INC. Обычно она применяется для инкремента указательного или индексного регистра при вычислении 26
Рис 2.8. Однооперандная команда, операнд которой 1 1 1 1 находится в 16-битном регистре | коп reg смещений, а также - 16-битного РОНа в арифметических операциях. С такими операндами команда принимает очень простую однобайтную форму, показанную на рис. 2.8. Она имеет 3-битное поле reg, определяющее один из восьми 16-битных регистров. Кодирование регистров в поле reg показано в первых двух столбцах табл. 2.1. Таблица 2.1. Кодирование регистров 1 байтный регистр 8-битный регистр ООО АХ AL 001 СХ CL 010 DX DL 011 ВХ BL 100 SP AH 101 BP CH 110 SI DH 111 DI BH Остальные 5 бит команды определяют операцию и называются кодом операции (КОП). Например, у команды INC код операции равен 01000. На рис. 2.9 показана команда, которая производит инкремент содержимого регистра BP. Такой способ адресации операнда иногда называется регистровым режимом. В табл. 2.2 показаны все режимы адресации операндов. коп reg Рис. 2.9. Команда инкремента содержимого регистра BP |р' 1' (/ 0*0 JJjTTJ INC BP Таблица 2.2. Режимы адресации операндов НЕПОСРЕДСТВЕННЫЙ РЕГИСТРОВЫЙ ПРЯМАЯ АДРЕСАЦИЯ ПАМЯТИ КОСВЕННАЯ АДРЕСАЦИЯ ПАМЯТИ базовый регистр индексный регистр базовый регистр + индексный регистр базовый регистр + смещение индексный регистр + смещение базовый регистр + индексный регистр + смещение — " 27
В наиболее общей форме команда INC может произвести инкремент содержимого любого РОНа, указательного или индексного регистра (8 или 16 бит) и любого байта или слова памяти* Эта форма длиной в два байта показана на рис. 2.10. Теперь поле КОП расщеплено на две части: семь бит его находятся в первом байте, а три - во втором. Код операции команды INC в такой форме равен 1111111 ООО. Однобитное поле w определяет длину операнда: если w ■ 0, операнда имеет 8 бит, а при w = 1 - 16 бит. Поле mod показывает, находится ли операнд в регистре или памяти. Если mod = 11, операнд содержится в регистре, а три остальные комбинации поля mod относятся к памяти. Когда операнд - в регистре, поле т/т показывает конкретный регистр; а если операнд - в памяти, это же поле определяет, где именно он находится (сокращение г/т обозначает регистр/память). коп 3 l™*** коп ; Рис. 2.10. Однооперандная команда, r/m I операнд которой находится в регистре или » I * памяти Вначале рассмотрим случай, когда операнд находится в регистре (mod- 11). Кодирование регистров в поле г/т показано в табл. 2.1. По существу, мы имеем второй вариант регистрового режима адресации операндов. На рис. 2.11 приведена команда инкремента содержимого регистра CL. Для доступа к 8-битному регистру требуется более длинная форма команды INC. коп w mod коп r/m 1ГГ7 О 0 0 0 0 11 Рис. 2.11. Команда инкремента содержимо- ■ ■ I ■ | I • ■ I го регистра CL 1111111 INC Байт Регистр INC CL Теперь обратимся к случаю, когда операнд находится в памяти (mod - = 00, 01,10). Этот режим иногда называется косвенной адресацией памяти, так как операнд содержится в памяти, но смещение явно не указано. Вместо этого оно получается суммированием довольно странных на первый взгляд значений. (Удобство такого режима мы пояснили в разд. 1.7.) Смещение равно сумме не более трех слагаемых: 16-битного значения (оно называется смешением в команде), содержимого индексного регистра (SI, DI или никакого), определенного в команде, и содержимого базового регистра (SP, BP или никакого), указанного в команде. Поле г/т идентифицирует базовый и индексный регистры в соответствии с табл. -2.3, а поле mod определяет смещение в команде (см. табл. 2.4). Образованный результат локализует операнд внутри сегмента. Операнд находится в текущем сегменте данных (но если в вычислении смещения участвует регистр BP, 28
операнд находится в текущем сегменте стека). Конечно, для образования 20-битного (24-битного в виртуальном режиме) адреса памяти потребуется еще одно сложение с привлечением сегментного регистра. Таблица 2.3. Базовый и индексный регистры, определяемые полем г/т для операндов в памяти (mod + Щ Поле г/т Базовый регистр Индексный регистр ООО ВХ SI 001 ВХ DI 010 BP SI 011 BP DI 100 Нет SI 101 Нет DI 110 BP Нет 111 ВХ Нет Если mod « 00 и г/т - ПО, см. примечание к табл. 2.4. Таблица 2.4. Смещение в команде, определяемое полем mod для операндов в памяти (mod * 11) mod Смещение в команде Пояснение 00 Нуль (16 бит) 01 8-битное содержимое следующего байта команды расширяется со знаком до 16 бит 10 16-битное содержимое двух следующих байт команды (сначала младший, а затем старший байты) Если mod » 00 и г/т «110, то: табл. 2л и 2.4 неприменимы; команда содержит два дополнительных байта; в этих байтах находится все смещение в сегменте (сначала младший, а затем старший байты). В качестве примера рассмотрим команду, показанную на рис. 2.12. Поле кода операции содержит 1111111 ООО и определяет команду INC. Поле w= 1 показывает длину операнда 16 бит. Поле mod « 01 и операнд, следова- Команда имеет дополнительный байт Команда имеет два дополнительных байта 29
коп w mod КОП r/m Смещение 1 1 111 0 1 0 0 0 1 0 0 0 10 1110 Рис. 2.12. Пример команды с операндом в памяти тельно, находится в памяти; кроме того, смещение в команде из следующего байта необходимо расширить со знаком до 16 бит: 0000 0000 0101 1100. Поле г/т = 100, поэтому со смещением в команде следует просуммировать содержимое регистра SI (пусть оно равно 1010 00001000 ОНО): Так как в вычислениях не участвует регистр BP, операнд находится в текущем сегменте данных. Предположим, что регистр DS содержит 1111 0000 1111 0000 и процессор работает в реальном режиме. Тогда адрес операнда в памяти равен: 1111 0000 1111 0000 (сегмент) 1010 0000 1110 0010 (смещение в сегменте) 1111 10101111 1110 0010 (адрес памяти) Длина операнда 16 бит (w = 1), поэтому им является содержимое двух байт с адресами 1111 1010 1111 1110 0010 (младший) и 1111 1010 1111 1110 ООН (старший). Операнд не обязательно должен находиться только в текущих сегментах данных и стека. Его можно считывать из любого сегмента, введя перед командой однобайтный префикс, обозначающий сегментный регистр (рис. 2.13). На рис. 2.14 показана та же команда, что и на рис. 2.12, но теперь операнд находится в текущем дополнительном сегменте. Набор, идентифицирующий байт префикса 00 = текущий дополнительный сегмент 01 = текущий сегмент кода 10 *= текущий сегмент стека 11 * текущий сегмент данных 1010 00001000 0110 +0000 0000 0101 1100 1010 00001110 0010 (содержимое SI) (смещение в команде) (результат) О 1 seg 1 1 О Рис. 2.13. Префикс замены сегмента 30
seg КОП w mod КОП r/m Смещение jo о 1 о o|i i oj J1 1 1 11 У1}1! 1° 11° 0 0 1 0 °] |o 1 0 1 1 i о o| ES Рис. 2.14. Применение префикса замены сегмента Мы рассмотрели задание смещения операнда в памяти с помощью базового и/или индексного регистров, но часто операнд находится в фиксированной ячейке одного из сегментов. В этом случае целесообразно определить все смещение непосредственно в команде. Такой режим адресации операндов называется прямой адресацией памяти: смещение находится в двух байтах самой команды (конечно, "наоборот"). Как обычно, команда должна определить также код операции и сам факт прямой адресации памяти. Было бы удобно использовать для указания режима комбинацию бит в полях mod и r/m. К сожалению, все комбинации уже исчерпаны для косвенной адресации памяти и регистровой адресации, поэтому для нашей цели придется привлечь какую-то редко используемую комбинацию. Такой комбинацией оказалась mod * 00 и r/m ■ 110. Как пример, на рис. 2.15 показана команда, которая производит инкремент байта со смещением 0101 101011110000 в текущем сегменте данных. КОП w moo КОП r/m Следующие два байта команды |1 1 1 1 1 1 1 3 0 о)1 1 о| I1 1 1 1 о о оо| |р 1 0 1 1 oTof ,NC Байт [ *С J Младшие биты Старшие биты \ / смещения смещения Смещение в следующих двух байтах Рис 2.15. Команда инкремента байта со смещением 0101 1010 1111 0000 в текущем сегменте данных Для прямой адресации пришлось пожертвовать косвенной адресацией через BP (без индексного регистра и с нулевым смещением в команде), но ее можно реализовать, указав mod = 01 и введя дополнительный байт для нулевого смещения в команде. Два операнда. Разобравшись с однооперандными командами, обратимся к командам, имеющим два операнда, например ADD. Мы уже говорили, что эта команда берет значение одного операнда, прибавляет его к значению второго операнда и запоминает результат на месте любого операнда. Если разрешить нахождение обоих операндов в памяти, в команде потребовались бы поля mod и г/т для каждого из них. Чтобы сократить длину команд, было решено, что, по крайней мере, один из операндов должен 31
коп id i mod reg Рис. 2.16. Типичная двухоперандная команда быть в регистре. Тогда в команде потребуются поля mod и г/т только для одного операнда, а для другого достаточно поля reg (рис. 2.16). Поле w показывает, являются операнды байтами (w = 0) или словами (и>= 1). В команде появилось также новое поле d (приемник); если d - 0, результат запоминается в операнде, определяемом полями mod и r/m, а если d = 1 - в операнде, определяемом полем reg. Операнд, в котором запоминается результат, называется приемником, а другой операнд называется источником. В качестве примера рассмотрим команду ADD, показанную на рис. 2.17. Она имеет код операции 000000. Поле w = 0 показывает, что оба операнда являются байтами. Определяемым полем reg операндом оказывается регистр СН. Поле mod = 11 означает, что поля mod и r/m определяют регистр, а поле r/m конкретизирует регистр BL. Поле d = 1 показывает запись результата в операнд, определяемый полем reg, т.е. в регистр СН. Следовательно, команда прибавляет содержимое регистра BL (источника) к содержимому регистра СН (приемника) и помещает результат в СН. Одним из операндов в двухоперандной команде может быть константа, содержащаяся в самой команде как непосредственный операнд. Этим достигаются два преимущества: уменьшается требуемая память (не нужно хранить адрес данных и сами данные) и процессор быстрее получает данные. коп |о о о о о о d w mod reg r/m ADD J Байт В операнд reg Рис. 2.17. Пример двухоперандной команды Примером команды с непосредственным операндом служит команда MOV (переслать). Она часто применяется для загрузки константы в регистр. Здесь непосредственный операнд можно определить полем reg, и команда приобретает простую форму, показанную на рис. 2.18. Поле н> по-прежнему задает длину операнда 8 (н> = 0) или 16 (н> = 1) бит; в первом случае непосредственный операнд занимает один байт в команде, а во втором - два байта. Как пример, на рис. 2.19 представлена команда, которая передает значение 1111 0000 0000 1111 в регистр DI. коп reg ] с Данные ] Е 1нные, если w=\ 32 Рис. 2.18. Простейшая команда с непосредственным операндом
КОП w reg Данные jl 0 1 1 1 1 1 l| |o О О 0 1 1 1 l| jj 1 1 1 Q Q Q QJ MOV Слово Ol Рис. 2.19. Пример команды с непосредственным операндом В несколько более сложной команде операнд-приемник определяется полями mod и г/т вместо поля reg, поэтому в команде появляется дополнительный байт (рис. 2.20). Показанная на рис. 2.21 команда передает значение 1111 0000 0000 1111 в слово, находящееся в сегменте данных со смещением из регистра DI. | КОП ^ wj J mod КОП r/m j | Данные j | Данные, если iv=11 Рис. 2.20. Команда с непосредственным операндом, использующая поля mod и г/т КОП mod КОП r/m |l 1 О 0 0 11 i El ООО 1 1 1 I I 0 0 0 0 1 1 1 1 Данные ! I 1 1 1 1 0 0 0 01 MOV Слово Память MOV 01 Рис. 2.21. Пример команды с непосредственным операндом, использующей поля mod и г/т Так как двухоперандная команда имеет только одно поле н>, оба операнда должны иметь длину 8 или 16 бит. Однако часто непосредственные операнды являются небольшими числами и не требуют 16 бит. Это утверждение особенно справедливо для команд сложения, вычитания и сравнения, хотя для операндов логических команд оно обычно не выдерживается. Следовательно, можно уменьшить размер команд с непосредственными операндами, если не отводить 16 бит для хранения небольших чисел. Поэтому в некоторых командах (сложение, вычитание и сравнение) введен бит s (s означает "с расширением знака")- Он занимает место бита d, так как в командах с непосредственными операндами возможно только одно направление. Поле s имеет смысл только для 16-битных операндов (w = 1) и показывает, содержатся ли в команде все 16 бит непосредственного операнда (s = 0) или только 8 бит (5=1), которые должны расширяться со знаком до 16 бит для образования 16-битного операнда. Такая форма команды иллюстрируется рис. 2.22. Соответствующий пример показан на рис. 2.23. Здесь к содержимому слова в памяти прибавляется значение 0000 0000 0000 1111 и результат помещается в это же слово. Слово находится в сегменте данных, а смещение его берется из регистра DI. Отметим, что благодаря биту s экономится один байт. зз 2 Заказ 6126
КОП |s|w| |mod| КОП J f/m \ j Данные | | Ланиые^ если*7Г| Рис. 2.22. Команда с нетосредственным операндом, содержащая поле s КОП 1 0 0 0 0 0 mod КОП r/m Данные ООО АОО |слово Память ADD 01 ] I 0 0 0 0 1 1 1 1 Расширение со знаком Рис. 2.23. Пример команды с непосредственным операндом, содержащей поле s 2.7. ЗАМЕЧАНИЯ О РЕЖИМАХ АДРЕСАЦИИ После знакомства с режимами адресации возникают следующие вопросы: 1. Нужно ли практически пользоваться полями mod, reg, r/m, w, s, d и др. при использовании любой команды с операндами? 2. Зачем предусмотрено так много режимов адресации? Ответ на первый вопрос отрицательный, если, конечно, вы не собираетесь писать программу на машинном языке. Имея такие средства программирования, как Ассемблер или компилятор, вам никогда не придется обращаться к полям mod, reg, r/m и др.; любой Ассемблер или компилятор освободит вас от этих деталей. Для понимания ответа на второй вопрос нужно знать, что семейство процессоров 86 разработано с учетом трансляции программ на языках высокого уровня в эффективный код, т.е. в программы на машинном языке. Были проанализированы типичные фрагменты программ на языках высокого уровня и их требования к режимам адресации. Рассмотрим некоторые из этих требований. Большинство языков программирования оперируют простыми переменными и массивами. Переменная называется простой, если она представляет единственное значение, а массивом называется переменная, которая представляет последовательность значений. Рассмотрим типичный оператор присваивания, имеющийся в большинстве языков высокого уровня: А(1) = Х Он читается так: "i-й элемент А становится равным Xм. Его можно преобразовать в команды, которые пересылают содержимое ячейки памяти, соответствующей простой переменной X, в регистр, например BL, а затем пересылают содержимое BL в ячейку памяти, соответствующую i-му элементу массива А. Пусть X - это содержимое ячейки со смещением 34
OFFO в текущем сегменте данных, а А(0) - это первый элемент массива А со смещением 0FF1 в этом же сегменте. Машинная команда, которая пересылает X в регистр BL, показана на рис. 2.24, а. В ней с помощью специальной комбинации полей mod и т/т задается прямая адресация памяти. Поскольку обращения к простым переменным (таким, как X) встречаются часто, неудивительно наличие такого режима адресации. На рис. 2.24, б показана машинная команда, которая передает содержимое регистра BL в ячейку А (I). Здесь предполагается, что в индексном регистре уже находится значение индекса I. Подобные обращения к массиву показывают необходимость режима косвенной адресации памяти "индексный регистр + смещение (в команде)". Операторы присваивания вида А(1) ■ В(J) требуют, по крайней мере, двух индексных регистров с указанным выше режимом адресации "SI + смещение (в команде)" и "DI + смещение (в команде)". Такие обращения к массиву, как А (I + 2), не встречают дополнительных сложностей; поле смещения в команде на рис. 2.24, б будет просто содержать 0FF3, т.е. смещение А(2), а не смещение А(0). коп d w 11 0 0 0 1 О MOV |Байт В операнд reg mod reg ] i О О О 1 1 1 1 о I I Смещение в следующих двух байтах Смещение 1 1 1 1 0 0 0 0 Младшие биты смещения а) I I О 0 0 0 1 1 1 1| Старшие биты смещения КОП d w 10 0 0 10 MOV I Байт В операнд г/т mod reg 1 О О 1 1 1 0 1 |Байт I BL I 1 r/m \ / I i 1 1 1 1 0 0 0 1 Младшие биты смещения Смещение равно (DI) +disp Смещение I 0 0 0 0 1 1 1 1| Старшие биты смещения б) Рис. 2.24. Машинные команды, реализующие оператор А (1) = X: a - пересылка X в BL, б — пересылка BL в а (!) В некоторых языках высокого уровня применяются указатели. Указатель - это переменная, содержащая адрес памяти некоторой другой переменной, которую мы назовем указываемой или адресуемой. Если значение указателя (т.е. значение в ячейке памяти, соответствующей указателю) изменяется, указываемая переменная будет соответствовать другой ячейке памяти. Удобный доступ к указываемой переменной заключается в том, чтобы поместить значение указателя в регистр ВХ, а затем воспользоваться режимом адресации с привлечением ВХ, т.е. режим "ВХ" следует применять для обращения к простой указываемой переменной (простой переменной, адресуемой указателем), а режимы "ВХ + SI" и 35
"ВХ + DI" - к элементу в указываемом массиве (массиве, адресуемом указателем). В языках высокого уровня применяются также записи. Под записью (иногда называемой структурой) понимается набор именованных элементов данных, возможно, различных типов. Запись не совпадает с понятием массива, представляющего собой последовательность (наименованных) элементов данных одного и того же типа. Например, в программе начисления заработной платы имеются записи, соответствующие каждому работнику. Запись может содержать фамилию работника, номер страхового полиса, год поступления на работу и почасовую ставку. Конкретный элемент записи, например год поступления на работу, находится в одной и той же позиции во всех записях, Если, например, эта информация находится в четвертом байте от начала записи и запись для Джона Доу начинается со смещением 03В4, то год поступления на работу этого сотрудника содержится в ячейке памяти со смещением 03В7. Следовательно, положение любого элемента в записи фиксировано, и к нему можно обратиться с помощью прямой адресации; по существу, здесь нет отличий от простой переменной. Рассмотрим теперь указываемую запись и предположим, что указатель записи находится в регистре ВХ. Режим адресации для обращения к элементу такой записи имеет вид "ВХ + смещение (в команде)4', причем смещение в команде соответствует позиции элемента в записи. Например, для указываемой записи о работнике смещение (в команде) байта о годе поступления на работу равно 3. Если запись невелика (менее 256 байт), для смещения в команде достаточно 8 бит и можно использовать режим с mod -01. Хотя режим адресации операнда для обращения к элементу указываемой записи похож на режим обращения к элементу массива (оба они имеют вид "регистр + смещение (в команде)", между ними имеется существенная разница. В случае массива элементов смещение в команде соответствует началу массива, а регистр— расстоянию в массиве. В случае же указываемых записей регистр соответствует началу записи, а смещение в команде - расстоянию в записи. Рассмотрим массив, в котором каждый элемент является записью о работнике. Более того, будем счи- тать, что это указываемый массив. Предположим, что указатель находится в регистре ВХ, а индекс, соответствующий элементу массива, в регистре SI. Тогда режим адресации для доступа к элементу года поступления конкретной записи имеет вид "ВХ + SI + смещение (в команде)", где смещение в команде равно 3. Такая ситуация оправдывает наличие в процессоре 80286 наиболее сложного режима адресации "базовый регистр + индексный регистр + смещение (в команде)". 36
Остается показать необходимость режимов адресации с привлечением BP как базового регистра и сегмента стека вместо сегмента данных. Эти режимы предназначены для эффективной реализации языков с блоковой структурой и реентрантных процедур. Процедура называется реентрантной, если ее можно вызывать тогда, когда она выполняется от предыдущего вызова. Подобная ситуация возникает, если: процедура вызывает саму себя; процедура вызывается некоторой другой процедурой, которая, в свою очередь, вызвана первоначальной процедурой; выполнение процедуры было приостановлено из-за прерывания, а при обработке прерывания эта же процедура была вызвана вновь. Все используемые реентрантной процедурой данные (локальные переменные и параметры) должны иметь уникальное размещение в памяти для каждого параллельного вызова процедуры, иначе данные, используемые одним вызовом процедуры, будут разрушены последующим вызовом. Это означает, что при каждом вызове процедуры для ее данных нужно распределять память. Такая область памяти называется стековым кадром (подробнее см. гл. 3). Конечно, весьма желательно, чтобы при своем завершении процедура освобождала бы эту область памяти. Поскольку последняя вызванная процедура заканчивается первой, удобным местом для распределения памяти оказывается стек. При каждом вызове процедуры для стекового кадра резервируется блок памяти в вершине стека путем простого изменения содержимого регистра SP. При выполнении процедуры необходим указатель начала стекового кадра, функции которого выполняет регистр BP. К элементам внутри стекового кадра можно обращаться в режимах адресации с привлечением регистра BP. Например, к простой переменной в стековом кадре можно обратиться в режиме, "BP + смещение (в команде)", а к элементу массива - в режиме "BP + SI + + смещение (в команде)". Поскольку в образовании адреса участвует регистр BP, обращение происходит к текущему сегменту стека (а не к текущему сегменту данных), в котором и находится стековый кадр. Применение режимов адресации в языках высокого уровня показано в табл. 2.5. Таблица 2.5. Применение прямого и косвенного режимов адресации в языках высокого уровня Данные Не указываемая Указываемая Стековый кадр Простая переменная Прямая Массивы SI + смещение DI + смещение ВХ BX + SI BX + DI BP + смещение BP + SI + смещение BP+ DI +смещение 37
Окончание табл. 2.5 Данные Не указываемая Указываемая Стековый кадр Записи Массивы записей Прямая SI + смещение DI + смещение ВХ + смещение ВХ + SI + смещение ВХ + DI + смещение BP + смещение BP + SI + смещение BP + DI + смещение Под "смещением" понимается смещение в команде. ЗАКЛЮЧЕНИЕ Мы рассмотрели структуру процессора 80286 и формат команд. В следующей главе мы обратимся к самим командам. Процессор 80286, работающий в реальном режиме, практически аналогичен микропроцессору 8086, поэтому литературные источники по микропроцессору 8086 относятся и к процессору 80286. Если в гл. 2 мы рассматривали операнды команд, то теперь коснемся операций, которые выполняются над операндами. Описание команд дается неформально, а более строгое описание можно найти в литературе. Система команд подразделяется на базовые и усовершенствованные команды, которые связаны с мультипрограммированием, управлением памятью и защитой; их нет в предыдущих процессорах семейства 86. Все базовые команды процессора 80286 имеются также в процессоре 80186, а большинство их - и в микропроцессоре 8086 (отсутствующие в нем команды будут отмечаться при описании). Усовершенствованные команды рассматриваются в гл. 5. Ради удобства изучения введены следующие группы базовых команд: команды передач данных, арифметические, логические команды, цепочечные, передачи управления, прерываний, флажковые, синхронизации и поддержки языков программирования высокого уровня. Мы подробно рассмотрим все эти группы команд. Напомним, что процессор 80286 имеет два режима работы: реальный и виртуальный. В реальном режиме усовершенствованные возможности запрещены, а в виртуальном режиме - разрешены. В этой главе предполагается, что процессор 80286 работает в реальном режиме. Когда допустимым операндом команды указывается регистр, им может быть любой РОН, указательный или индексный регистр, но не может быть сегментный регистр или IP. Специально оговаривается, когда в команде допустим как операнд сегментный регистр. Регистр IP никогда нельзя указывать как операнд. ГЛАВА 3 БАЗОВАЯ СИСТЕМА КОМАНД ПРОЦЕССОРА 80286 38
ЭЛ. НОТАЦИЯ ЯЗЫКА АССЕМБЛЕРА Мы будем описывать команды, пользуясь нотацией языка ассемблера ASM286 фирмы Intel. Конечно, мы не преследуем цели научить программированию на этом языке, а пользуемся им как средством описания процессора. Подробное описание языка ассемблера содержится в руководствах фирмы Intel. Как обычно, оператор состоит из полей метки, команды, операнда и комментария (из них обязательным является только поле команды). Поле операнда может содержать источник и приемник, причем приемник указывается первым. Например, команда MOV вх,сх передает в регистр ВХ (приемник) содержимое регистра СХ (источник). В командах ASM286 допустимы операнды трех типов: регистр, непосредственный операнд и память. Например» команда INC DX осуществляет инкремент регистра DX. Непосредственный операнд - это значение, содержащееся в самой команде; например, команда ADD СХ,35 прибавляет значение 35 (непосредственный операнд) к содержимому регистра СХ (регистровый операнд) и помещает результат в СХ. Процессор 80286 имеет два режима адресации операндов в памяти: прямой и косвенный. В прямой адресации памяти адрес операнда содержится в команде. При косвенной адресации памяти адрес приемника образуется суммированием двух или нескольких элементов: содержимого базового регистра (ВХ или BP), содержимого индексного регистра (SI или DI) и значения, находящегося в команде. Вот несколько примеров адресации памяти в ASM286: INC MEMLOC ; Пример прямой адресации памяти INC CSI3 Л Примеры INC CSI3CBXD ; косвенной INC MEMBYTECDI1 $ адресации INC VALUE С DI Э С BP 2 ; памяти Ради простоты в этой главе все операнды в памяти даются в режиме прямой адресации, но вместо него можно всегда указывать любой режим косвенной адресации. 39
Отличительная особенность языков ассемблера для процессоров семейства 86 заключается в том, что они являются типизированными. Другими словами, с каждым операндом ассоциируется тип (байт, слово и др.), который используется Ассемблером для определения кода операции генерируемой команды. В нетипизированном языке код операции полностью определяется мнемоникой команды, например INCW LOC ; Инкремент слова по адресу LOC INCB LOC ; Инкремент байта по адресу LOC В типизированном языке переменная LOC должна быть определена как байт или слово (оператором, который распределяет память для переменной LOC), и команда INC LOC осуществляет инкремент байта или слова в зависимости от типа переменной LOC. Аналогично и каждый регистр имеет тип (например, регистр ВХ - слово, регистр CL - байт), поэтому в ассемблере ASM286 команды инкремента байта или слова в регистре имеют вид: INC ВХ ; Инкремент регистра длиной слово INC CL 5 Инкремент регистра длиной байт 3.2. КОМАНДЫ ПЕРЕДАЧ ДАННЫХ Процессор 80286 имеет четыре класса команд передач данных: общие передачи, аккумуляторные передачи, адресные передачи и флажковые передачи (см. табл. 3.1). Таблица 3.1. Команды передач данных Мнемоника команды Описание команды Общие MOV (переслать) ИСТОЧНИК - ПРИЕМНИК XCHG (обменять) ИСТОЧНИК ~ ПРИЕМНИК PUSH (включить в стек) ИСТОЧНИК - Стек POP (извлечь из стека) Стек - ПРИЕМНИК PUSHA (включить в стек всё) Регистры ■+ стек РОРА (извлечь из стека всё) Стек ■* регистры Аккумуляторные IN (ввести) Порт •+ AL или АХ OUT (вывести) AL или АХ ■* порт ХЬДТ (преобразовать) / (AL) -+ AL 40
Окончание табл. 3.1 Мнемоника команды Описание команды Адресные LEA (загрузить эффективный адрес в регистр) LDS (загрузить указатель в регистр и DS) LES (загрузить указатель в регистр и ES) ИСТОЧНИК, ИСТОЧНИК + 1 РЕГИСТР ИСТОЧНИК + 2, ИСТОЧНИК + 3 DS ИСТОЧНИК, ИСТОЧНИК + 1 - РЕГИСТР ИСТОЧНИК + 2, ИСТОЧНИК + 3 - ES Смещение ИСТОЧНИКА-" РЕГИСТР Флажковые LAHF (загрузить флажки в АН) SAHF (запомнить АН во флажках) PUSHF (включить в стек флажки) POPF (извлечь из стека флажки) SF, ZF, AF, PF, CF - АН АН- SF, ZF, AF, PF, CF Флажки стек Стек—флажки Общие передачи. Командами общих передач являются MOV (переслать), XCHG (обменять), PUSH (включить в стек), POP (извлечь из стека), PUSHA (включить в стек всё) и РОРА (извлечь из стека всё). В качестве одного из операндов в этих командах допустим сегментный регистр, поэтому в сегментные регистры можно поместить новые значения, а старые их значения запомнить. Программист обычно не привлекает значения из сегментных регистров, поэтому во всех остальных командах использование сегментных регистров в качестве операндов не допускается. Команда MOV передает байт или слово из источника в приемник. Один из операндов ее может находиться в регистре или памяти, а другой - в регистре, сегментном регистре или в самой команде (непосредственный операнд). Примеры различных команд MOV показаны в табл. 3.2. Таблица 3.2. Примеры команд MOV Передача данных Слово Байт Регистр в регистр Непосредственный операнд в регистр Память в регистр Регистр в память Регистр в сегментный регистр Сегментный регистр в регистр Память в сегментный регистр Сегментный регистр в память MOV АХ, ВХ MOV СХ, 850 MOV АН, ВН MOV BL, 35 MOV DX, MEMW MOV MEMW, СХ MOV ES, ВХ MOV CL, МЕМВ MOV МЕМВ, DL MOV АХ, DS MOV SS, MEMW MOV MEMW, CS 41
Команда XCHG осуществляет обмен байт или слов. Один из ее операндов может быть в регистре или памяти, а другой - в регистре. Различий между приемником и источником нет. Примеры команд XCHG показаны в табл. 3.3. Таблица 3.3. Примеры команд XCHG Обмен данными Слово Байт Регистр с регистром XCHG СХ, DX XCHG AL, АН Регистр с памятью XCHG ВХ, MEMW XCHG BL, МЕМВ Команда PUSH передает слово из источника в стек, а команда POP осуществляет противоположное действие: она передает слово из стека в приемник. Стек - это область памяти, в которой размещается текущий сегмент стека. Регистр SP содержит смещение последнего включенного в стек слова; оно называется вершиной стека. По мере включения в стек новых слов они располагаются по меньшим адресам памяти; говорят, mfo стек растет в направлении уменьшения адресов. Команда PUSH начинается с декремента содержимого регистра SP на 2, т.е. адресует следующее свободное слово в стеке. Команда POP завершается инкрементом содержимого SP на 2, т.е. удаляет считанное слово из стека. На рис. 3.1. показано действие команд PUSH и POP. Операндом команд PUSH и POP может быть сегментный регистр, 16-битный (несегментный) регистр или слово из памяти. Кроме того, в команде PUSH можно указывать непосредственный операнд, что не допускается в команде PUSH микропроцессора 8086. Поскольку извлечение слова из стека в команду не имеет смысла, в команде POP не может быть непосредственного операнда. Примеры команд PUSH и POP показаны в табл. 3.4. Таблица 3.4. Примеры команд PUSH и POP Операнд Включение Извлечение Регистр PUSH АХ POPBX Память PUSH MEMW POP MEMW. Сегментный регистр PUSHDS POPES Непосредственный операнд PUSH 895 Все регистры PUSHA POPA 42
SS J О О A e j- Сегмент стека -0OA80 SP: I О О ОС 00А8А -00А8С I Доступны для будущих включений Сегмент стека SS | О О А 8 ^- -00А80 SP J 0 0 0 А | 00А8А 00А8С 1 F А 0 Верхний элемент стека Предыдущие элементы в стеке Доступны для будущих включений Верхний элемент стека ] Предыдущие элементы в стеке Сегмент стека SS -00А80 SP. 00А8А -00А8С 1 F А О Рис. 3.1. Пример стековых операций а — начальная конфигурация стека, б — стек после команды PUSH, ■ которая включает в него значение A01F, в - стек после команды POP у Доступны для будущих включений Верхний элемент стека Предыдущие элементы в стеке 43 1
Сделаем одно замечание. Рассмотрим, что произойдет, если команда изменила содержимое регистра CS. Ясно, что при этом текущим сегментом кода станет новый сегмент. Однако обычный инкремент содержимого регистра IP образует в нем смещение следующей по порядку команды в предыдущем сегменте кода. Следовательно, комбинация CS и IP будет определять бессмысленный адрес памяти, а процессор попытается выбрать следующую команду по этому адресу. Мы приходим к выводу, что если команда, изменяющая содержимое регистра CS, не загрузит соответствующее значение в регистр IP, процессор будет осуществлять бессмысленные передачи. По этой причине некоторые команды, которые допускают использование в качестве операнда сегментного регистра, не могут использовать один конкретный сегментный регистр - а именно, регистр CS. Примерами служат команды: MOV CS,... ; Недействительная операция Р0Р cs 5 Еше одна недействительная операция Имеются еще две команды включения в стек и извлечения из стека: PUSH А - включить в стек все регистры и POP А - извлечь все регистры. Эти команды первоначально планировалось реализовать и в микропроцессоре 8086, но этого не было сделано и впервые они появились в процессоре 80186. Команды PUSHA и РОРА являются эффективным средством подпрограммы или процедуры, позволяющим сохранить содержимое всех регистров (кроме сегментных регистров и регистра IP) в начале ее выполнения и восстановить его в конце. После сохранения содержимого регистров процедура может распоряжаться ими как угодно, а важная информация, бывшая в регистрах, будет возвращена вызывающей программе без изменений. Команда PUSHA включает в стек регистры в таком порядке: АХ, СХ, DX, ВХ, SP, BP, SI, DI. Включаемым значением регистра SP является то его значение, которое было в нем до выполнения команды PUSHA. При выполнении команды PUSHA происходит декремент содержимого регистра SP на 2 при включении в стек содержимого каждого регистра. Извлечение из стека, реализуемое командой РОРА, вызовет инкремент содержимого регистра SP на ту же величину, поэтому команде РОРА не требуется запомненное в стеке содержимое регистра SP и она его просто уничтожает. Аккумуляторные передачи. Передачи этого класса реализуют команды IN (ввести), OUT (вывести) и XLAT (преобразовать). В этих командах ре гистры трактуются не так единообразно, как в предыдущих командах (-л исключением сегментных регистров), так как операндом может быть только АККУМУЛЯТОР. Такая специализация аккумулятора позволяет уменьшить число байт в команде. 44
Команда IN передает данные (байт или слово) из входного порта в аккумулятор (AL или АХ). Аналогично команда OUT передает данные из аккумулятора в выходной порт. Номер порта разрешается определять прямо в самой команде или косвенно через регистр DX. Отметим, что никакой другой регистр нельзя привлекать для этой цели. Прямо в команде можно определить 256 портов, а через регистр DX - 64К портов. В случае прямой адресации портов не требуется предварительная загрузка номера порта в регистр DX. Косвенная адресация позволяет реализовать обращения к последовательным портам в программном цикле. Примеры команд IN и OUT показаны в табл. 3.5, а следующий фрагмент иллюстрирует различия прямой и косвенной адресации: IN AL,5 ; Прямой порт MOV DX.,5 ; Косвенный IN AL,DX ; порт Таблица 3.5. Примеры команд IN я OUT Команда Слово Байт IN (прямой) IN АХ*1 IN AL, 10 OUT (прямой) OUT 50, АХ OUT30,AL IN (косвенный) IN АХ, DX IN AL,DX OUT (косвенный) OUT DX,AX - OUT DX, AL Команда XLAT преобразует значение в регистре AL: она заменяет его на байт из таблицы, адресуемой регистром ВХ (еще одна специальная функция РОНа), причем индексом таблицы служит исходное содержимое регистра AL. Эта команда удобна для преобразования из одного кода в другой. Рассмотрим, например, такое кодирование десятичных цифр: Цифра Код 0 11000 1 00011 2 00101 3 00110 4 01001 5 01010 6 01100 7 10001 8 10010 9 10100 45
Такое кодирование представляет некоторый практический интерес, так как каждый код содержит точно два единичных бита (иногда этот код называется кодом "2-из-5") и применяется в телефонии. Предположим, что необходимо преобразовать цифру 7 в код "2-из-5". Получается следующий фрагмент: MOV MOV XLAT ВX,OFFSET TABLE AL,7 TABLE Поместить смещение таблицы в ВХ Загрузить 7 в AL Считать седьмой элемент из таблицы в AL Собственно преобразование показано на рис. 3.2. Отметим, что в команде всегда используется таблица, адресуемая регистром ВХ, и в ней нет поля для указания конкретной таблицы. Поэтому кажется несколько странным, что в ассемблерной записи команды XLAT указан операнд TABLE. Это сделано только для того, чтобы ассемблер мог проконтролировать нахождение таблицы TABLE в доступном сегменте. ВХ: Таблица преобразования б) Смещение в текущем сегменте данных 46 00011000 00000011 00000101 00000110 00001001 00001010 00001100 00010001 00010010 00010100 AL: | ОООООш] в) AL: 100010001 | Рис. 3.2. Пример использования команды XLAT для преобразования двоичной цифры 7 в код "2-ИЗ-5" а — таблица преобразования для кода "2-ИЗ-5", б — содержимое регистра AL до выполнения команды XLAT, в — содержимое регистра AL после выполнения команды XLAT
Адресные передачи. Адресные передачи реализуют команды LEA (загрузить эффективный адрес), LDS (загрузить указатель в DS) и LES (загрузить указатель в ES). Они позволяют программисту управлять механизмом адресации. Каждая из команд имеет два операнда: один - в памяти, а другой - в 16-битном регистре. Примеры команд: LEA BX,MEMLOC LDS CX,MEMLOC LES DX,MEMLDC Команда LEA вычисляет 16-битное смещение операнда-источника и загружает его в 16-битный регистр-приемник. Она удобна для передачи смещения переменной из одной части программы в другую, чтобы в этой другой части можно было при необходимости модифицировать значение переменной. Объекты, передаваемые между различными частями программы, называются параметрами, а различные части программы называются процедурами. Рассмотрим пример применения команды LEA. Предположим, что одна из процедур отвечает за инкремент переменных. Другие части программы могут вызвать ее и заставить произвести инкремент конкретной переменной. Смещение переменной, инкремент которой необходимо произвести, можно передать процедуре инкремента как параметр, поместив его до вызова в определенный регистр, например ВХ. Именно для этого и предназначена команда LEA. В ней нужно указать регистр ВХ как приемник, а переменную - как источник. Команда загрузит смещение переменной в регистр ВХ. Разумеется, команду LEA нужно выполнить до вызова процедуры. Процедура же обратится к значению переменной, пользуясь косвенной адресацией через регистр ВХ, т.е. в виде [ВХ]. Отметим, что при передаче процедуре инкремента значения переменной, а не ее смещения, процедура знала бы значение, но не смогла бы изменить его. Команда LDS передает четыре смежных байта из источника в пару 16-битных регистров-приемников. Источник должен находиться в памяти. Одним из регистров будет 16-битный регистр, указанный в команде, а вторым - регистр DS. Команда LES выполняет аналогичную функцию, но вторым регистром-приемником в ней является регистр ES, а не DS. Фактические передаваемые данные показаны на рис. 3.3. Команды LDS и LES служат эффективным средством установки полного начального адреса (сегментхмещение) переменной, к которой могут обращаться следующие команды. Комбинация начального адреса сегмента и смещения называется еще 32-битным указателем или просто указателем; команды LDS и LES передают указатель из памяти в регистры, привлекаемые в режимах адресации операндов. Предположим, например, что четыре смежных байта (первый из которых имеет адрес POINTER) содержат указатель однобайт- 47
ной переменной, как показано на рис. 3.4. Следующие две команды передают значение переменной в регистр AL: LDS ВХ,POINTER ; Загрузить указатель в DS и ВХ MOV AL,CBXD • Использовать режим адресации с DS и ВХ для обращения ; к адресуемой переменной Смещение, определяемое полями mod и г/т команды 16-битный регистр, определяемый полем reg команды DS или ES 8 Рис. 3.3. Пересылки данных в командах LDS и LES Текущий сегмент данных DS -80000 of1c L^80F1C Сегмент, содержащий адресуемую переменную -14В00 10С8 L^15BC8 к с II II I2 Рис. 3.4. Пример использования команды LDS Флажковые передачи. Команды этого класса обеспечивают доступ к флажкам процессора. К ним относятся команды LAHF (загрузить флажки в АН), SAHF (запомнить АН во флажках), PUSHF (включить в стек флажки) и P0PF (извлечь из стека флажки). Команда LAHF передает пять флажков SF, ZF, AF, PF и CF в определенные биты регистра АН, а команда SAHF реализует противоположную 48 •■
передачу. Указанные пять флажков выделены только потому, что они имелись в микропроцессоре 8080. (Команды LAHF и SAHF предусмотрены, в основном, для преобразования программ микропроцессора 8080 в эффективные программы для процессоров семейства 86.) Соответствие между битами регистра АН и флажками показано на рис. 3.5. АН: -SF Г -ZF -AF -PF CF Увеличение адресов памяти Рис. 3.5. Соответствие между флажками и битами регистра АН Рис. 3.6. Соответствие между флажками и битами в стеке NT- IOPL- -SF -ZF -AF Г PF CF Верхний элемент стека 1_„ -IF -OF -OF Команда PUSHF включает в слово стека все флажки процессора, а команда POPF извлекает слово из стека и передает его в регистр флажков. Соответствие между словом в стеке и флажками показано на рис. 3.6. 3.3. АРИФМЕТИЧЕСКИЕ КОМАНДЫ Процессор 80286 имеет четыре базовые арифметические операции в нескольких различных формах (табл. 3.6). Операции выполняются над 8-и 16-битными знаковыми и беззнаковыми операндами. Кроме того, предусмотрены команды для обработки десятичных чисел. Таблица 3.6. Арифметические команды Мнемоника команды Описание команды Сложение ADD (сложить) ПРИЕМНИК + ИСТОЧНИК - ПРИЕМНИК ADC (сложить с переносом) ПРИЕМНИК + ИСТОЧНИК + CF - ПРИЕМНИК INC (инкремент) ПРИЕМНИК + 1 - ПРИЕМНИК 49
Окончание табл. 3.6 Мнемоника команды Описание команды SUB (вычесть) SBB (вычесть с заемом) DEC (декремент) NEG (изменить знак) СМР (сравнить) MUL (умножить) IMUL (умножить со знаком) DIV (разделить) IDIV (разделить со знаком) Вычитание ПРИЕМНИК - ИСТОЧНИК ■* ПРИЕМНИК ПРИЕМНИК - ИСТОЧНИК -CF- ПРИЕМНИК ПРИЕМНИК - 1 - ПРИЕМНИК О - ПРИЕМНИК - ПРИЕМНИК ПРИЕМНИК - ИСТОЧНИК - ? Умножение AL * ИСТОЧНИК 8- АХ АХ * ИСТОЧНИК16 - DX, АХ Команда MUL, но операнды знаковые Деление АХ/ИСТОЧНИК 8 - AL остаток •* АН DX, АХ/ИСТОЧНИК 1б - АХ, остаток -*DX Команда DIV, но операнды знаковые. Различие между знаковыми и беззнаковыми числами заключается в интерпретации двоичных наборов. Беззнаковые числа - это обычные двоичные числа (все биты значащие), а знаковые числа представлены в дополнительном коде. На рис. 3.7 показаны диапазон и представление знаковых и беззнаковых чисел. Операции сложения и вычитания одинаковы для обоих типов чисел, т.е. обычные команды двоичного сложения и вычитания, разработанные для беззнаковых чисел, дают правильный результат и применительно к знаковым числам. Единственное различие между знаковыми и беззнаковыми сложением и вычитанием заключается в механизме обнаружения выхода за диапазон. Команды сложения и вычитания устанавливают флажок CF, если результат, интерпретируемый как беззнаковое число, оказывается вне диапазона; они уже устанавливают флажок OF, если результат, интерпретируемый как знаковое число, выходит за диапазон. Возможны ситуации, когда знаковый или беззнаковый результат будет вне диапазона, хотя другой результат оказывается в диапазоне (рис. 3.8). Большинство арифметических команд устанавливают или сбрасывают шесть флажков состояния, показывающих определенные свойства результата. В общем, флажки фиксируют следующие особенности: 50
Беззнаковые Знаковые Число Представление Число Представление 0 0000 0000 -128 1000 0000 1 0000 0001 -127 1000 0001 2 0000 0010 -126 1000 0010 126 0111 1110 1 1111 1111 127 0111 1111 0 0000 0000 128 1000 0000 1 0000 0001 2S3 1111 1101 ♦ 125 0111 1101 254 1111 1110 ♦126 0111 1110 255 1111 1111 ♦ 127 0111 1111 а) б) Число Представлен »е Число Представление 0 0000 0000 0000 0000 -32.768 1000 0000 0000 0000 1 0000 0000 0000 0001 -32.767 1000 0000 0000 0001 2 0000 0000 0000 0010 -32.766 1000 0000 0000 0010 32.766 0111 1111 1111 1110 -1 1111 1111 1111 1111 32.767 0П1 1111 1111 1111 0 0000 0000 0000 0000 32.768 1000 0000 0000 0000 ♦ 1 0000 0000 0000 0001 65.533 1111 1111 1111 1101 32.765 0111 1111 1111 1101 65,534 1111 1111 1111 1110 32.766 0111 1111 1111 1110 65.535 1111 1111 1111 1111 32.767 0111 1111 1111 1111 е) г) Рис. 3.7. Диапазоны 8- и 16-битных знаковых и беззнаковых чисел: а — беззнаковые 8-битные числа, б — знаковые 8-битные числа, в — беззнаковые 16-битные числа, г — знаковые 16-битные числа флажок CF устанавливается, если операция дала беззнаковый результат вне диапазона; флажок OF устанавливается, если в операции получился знаковый результат, находящийся вне диапазона (так называемое знаковое переполнение); флажок ZF устанавливается, если результат операции (знаковый или беззнаковый) равен нулю; - флажок SF устанавливается, если старший бит результата операции содержит 1, показывая отрицательное число;
Представление Интерпретация как беззнаковых чисел Интерпретация как знаковых чисел а) Знаковый и беззнаковый результаты в диапазоне 0000 0100 ♦ 0000 1011 0000 1111 4 11 15 CF---0 +4 •Ml + 15 Of =0 * б) Беззнаковый результат вне диапазона 0000 0111 ♦ 1111 1011 0000 0010 7 251 ~2 CF = 1 ***вне диапазона*** + 7 -5 +2 OF 0 в) Знаковый результат вне диапазона 0000 1001 ♦ 0111 1100 1000 0101 9 124 133 CF=0 i f9 + 124 ^23 OF-1 ***вне диапазона*** г) Оба результата вне диапазона 1000 0111 ■+ 1111 0101 0111 1100 135 245 124 CF=1 ••«вне диапазона*** -121 -11 + 124 OF = 1 ***вне диапазона*** Рис. 3.8. Примеры выхода за диапазон при сложении знаковых и беззнаковых чисел флажок PF устанавливается, если результат операции содержит четное число единичных бит (так называемый четный паритет); флажок AF устанавливается, если в десятичных операциях требуется коррекция (подробнее см. далее). Поведение флажков в арифметических операциях показано далее в табл. 3.22. В арифметике многократной точности беззнаковые числа, длина которых более 16 бит, разделяются на 8- или 16-битные поля, а затем операции циклически выполняются над последовательными полями, начиная с младших. Если любая из этих операций дает результат вне диапазона, результат оказывается правильным, но 1 либо переносится (сложение), либо занимается (вычитание) в операции над следующими полями. Рассмотрим, например, сложение 24-битных чисел ООП 1010 0000 0111 1011 0010 и 0010 0000 1100 0010 0101 ООП. Его можно выполнить за три сложения 8-битных чисел. 1. Складываются младшие 8 бит: 10110010 *0101 ООП 0000 0101 CF=1 52
2. Суммируются средние 8 бит с учетом переноса, образованного в предыдущем сложении: 1 (последний CF) 0000 0111 +11000010 11001010 CF = 0 3. Наконец, складываются старшие 8 бит с учетом переноса от предыдущего сложения: 0 (последний CF) ООП 1010 * 0010 0000 0101 1010 CF = 0 Получен результат 0101 101011001010 0000 0101. Этот пример показывает необходимость наличия команды (сложить с переносом), которая суммирует значения двух операндов и значение флажка CF. Аналогичная команда (вычесть с заемом) удобна для вычитания с многократной точностью. В арифметике многократной точности следует учитывать, что результат сложения и вычитания может оказаться вне диапазона. Это обычная ситуация, и она не свидетельствует об ошибке. Но выходящий за диапазон знаковый результат обычно не ожидается. Он показывает, что возникла ошибка и до продолжения расчетов результат необходимо скорректировать. Команды сложения. К командам сложения относятся ADD, ADC и INC. В общем, они применимы к любым операндам. Команда ADD (табл. 3.7) суммирует байты или слова источника и приемника и помещает результат в приемник. Один из операндов может находиться в регистре или памяти, а другой - в регистре или в самой команде (непосредственный операнд). Команда ADC аналогична команде ADD, но она привлекает для сложения начальное значение CF, что упрощает реализацию арифметики с многократной точностью. Формы команды ADC такие же, как и у команды ADD (табл. 3.7). Команда INC имеет всего один операнд. Она прибавляет 1 к содержимому операнда и помещает результат в этот же операнд. Примеры команды INC показаны в табл. 3.8. Команда INC идентична команде ADD с непосредственным операндом 1, но требует меньше байт. Она включена в систему команд потому, что прибавление (и вычитание) 1 встречается очень часто и должно осуществляться командой с минимальной длиной. 53
Таблица 3.7. Примеры двухоперандных арифметических и логических команд Операнды Слово Байт Регистр с регистром в регистр Регистр с памятью в регистр Регистр с памятью в память Регистр с непосредственным операндом в регистр Память с непосредственным операндом в память КОП АХ,ВХ КОП СХ, MEMW КОП MEMW, СХ КОП DX, 490 КОП MEMW, 490 КОПВЬ, CL KOnDL, МЕМВ КОП МЕМВ, DL КОПВЬ, 50 КОП МЕМВ, 50 КОП обозначает: ADD, ADC, SUB, SBB, CMP, AND, OR, XOR, TEST. Таблица 3.8. Примеры однооперандных арифметических и логических команд Операнд Слово Байт Регистр Память КОПВХ КОП MEMW KOnDL КОП МЕМВ КОП обозначает: INC, DEC, NEG, NOT. Команды вычитания. К командам вычитания относятся SUB, SBB, DEC, NEG и CMP. Первые три из них аналогичны трем командам сложения. Примеры команд SUB, SBB и CMP показаны в табл. 3.7, а команд DEC и NEG - в табл. 3.8. Команда NEG изменяет знак своего операнда. Если, например, операнд содержит представление -1(1111 1111), то команда NEG изменит его на+1 (0000 0001). Команда СМР аналогична команде вычитания, но результат не запоминается в приемнике. По существу, результат не запоминается нигде; он просто теряется в процессоре. Наверное, вы удивитесь: "Зачем нужна команда, результат которой теряется?" Оказывается, что состояние флажков, отражающих определенные свойства результата, важнее чем сам результат. Благодаря им можно определить отношение между двумя операндами, участвующими в вычитании. Если, например, ZF = 1, то результат равен нулю и значения операндов должны быть одинаковыми. 54
Состояния флажков для всех возможных отношений показаны в табл. 3.9. После команды СМР обычно находится команда условного перехода (см. далее), которая проверяет по флажкам, удовлетворяется ли конкретное отношение. Таблица 3.9. Состояния флажков после выполнения команды СМР Отношение между приемником и источником CF ZF SF OF РАВНЫ 0 1 0 0 Знаковые операнды МЕНЬШЕ — 0 1 0 МЕНЬШЕ - 0 0 1 БОЛЬШЕ - 0 0 0 БОЛЬШЕ - 0 1 1 Беззнаковые операнды НИЖЕ 1 0 — - ВЫШЕ 0 0 — — Вместо прочерка может быть 0 или 1 в зависимости от значений операндов. Команды умножения и деления. Умножение двух 8-битных чисел может дать произведение длиной до 16 бит. Пример умножения беззнаковых чисел показан на рис. 3.9. Аналогично произведение двух 16- битных чисел может иметь длину 32 бита. Команды умножения процессора 80286 позволяют умножать 8- или 16-битную величину, находящуюся в регистрах AL или АХ, на операнд такого же размера, определяемый в команде. Произведение длиной 16 или 32 бита помещается в регистре АХ и при необходимости в регистре DX, как показано на рис. 3.10. AL I (8 бит) X I Операнд I (8 бит) | АН | AL~| (1в бит) | ОХ | АХ "">2 бита) АХ Рис. ЗЛО. Операнды в команде умножения 1111 1111 X 1111 1111 1111 1111 1 1111 111 11 1111 11 111 1111 1 1111 1111 1 1111 111 11 1111 11 111 1111 1111 1110 0000 0001 Ив бит) Рис. 3,9. Пример, показывающий, что произведение может быть вдвое длиннее операндов I АХ joe вит) XI Операнд 1(16 бит) 55
Команды деления рассчитаны на противоположное по отношению к умножению действие. Они делят 16-битное число из регистра АХ (или 32-битное число из регистров АХ и DX) на операнд половинного размера, определяемый в команде. Частное помещается в регистр AL (АХ), а остаток - в АН (DX), как показано на рис. 3.11. Остаток ПП I АН I а) | Операнд j J | АН j ' А) б) | Операнд | ^ £ AL Остаток АХ DX DX АХ Рис. 3.11. Операнды в команде деления a — 8-битный делитель, б — 16-битный делитель В отличие от команд сложения и вычитания команды обычного двоичного умножения и деления, работающие с беззнаковыми числами, не дают правильных результатов для знаковых чисел (рис. 3.12). Следовательно, для знаковых чисел потребуются специальные команды умножения и деления. В процессоре 80286 имеются команды MUL (беззнаковое умножение), IMUL (знаковое умножение, которое иногда называется целочисленным умножением), DIV (беззнаковое деление) и IDIV (знаковое деление, которое иногда называется целочисленным делением). Примеры команд умножения и деления приведены в табл. 3.10. Представление 1111 1111 X 1111 1111 1111 1111 1 1111 111 11 1111 11 111 1111 1 1111 1111 1 1111 111 11 1111 11 111 1111 1 1111 1110 0000 0001 Интерпретация как беззнаковых чисел 25S X 255 Интерпретация как знаковых чисел -1 X -1 65.025 (правильный результат) -511 (неправильный результат) Рис. 3.12. Пример, показывающий, что обычное двоичное умножение не дает правильного результата для знаковых чисел 56
Таблица ЗЛО. Примеры команд умножения и деления Операнд Слова Байты АХ (AL) с регистром копвх КОПСЬ АХ (AL) с памятью КОП MEMW КОП МЕМВ КОП обозначает команды MUL, IMUL, DIV и IDIV. Поскольку при программировании часто возникает необходимость умножения на константу, в команде IMUL (но не в командах MUL, DIY или IDIV) разрешается определять множитель как непосредственный операнд. При этом множимое не обязательно должно быть в регистре АХ (AL), а может находиться в любом 16-битном регистре или в 16-битной ячейке памяти. Результат длиной 16 бит (а не 32 бита) допускается разместить в любом 16-битном регистре. Примеры такой команды IMUL показаны в табл. 3.11. Команды умножения IMUL с непосредственным операндом в микропроцессоре 8086 нет. Такую команду удобно применять для вычисления адресов элементов массивов с нечетным размером. Таблица 3.11. Примеры команды IMUL с непосредственным операндом Слово в регистре IMUL DX, ВХ, 115. ; ВХ * 115 - DX Слово из памяти IMUL СХ, MEMW, 632 ; MEMW * 632 » СХ Сделаем замечание о знаковом делении. Если разделить -26 на +7, можно получить частное - 4 и остаток +2, но можно получить частное -3 и остаток -5. Любая пара чисел дает правильный результат, но в первом случае остаток положителен, а во втором - отрицателен. Команда знакового деления IDIV действует так, что остаток всегда имеет такой же знак, как и делимое. В приведенном примере будут получены частное -3 и остаток -5. Определенное таким образом деление дает частное (и остаток) с одним и тем же абсолютным значением при делении -26 на +7, +26 на +7 и +26 на-7. В табл. 3.12 показано число бит операндов и результатов различных арифметических команд. Команды действуют так, чтобы результат двойной длины при умножении можно было использовать в последующем делении. А если вам нужно использовать результат умножения для чего-то еще, кроме деления? Как, например, умножить 17 (0001 0001) на 10 (0000 57
1010) и прибавить к произведению 20 (0001 0100)? Для этого нужно просто отбросить старшие 8 бит произведения. Однако теперь возникает проблема деления числа, которое не образовано предыдущим умножением. Попробуйте, например, поделить 8-битное число 35 (0010 ООН) на 7 (0000 0111). В команде деления предполагается, что 16-битное делимое находится в регистре АХ. Простое размещение 8-битного делимого в регистре AL не дает правильного результата, так как в делении будет участвовать "мусор", находящийся в регистре АН. Однако трудность преодолевается, если очистить регистр АН перед 8-битным делением или регистр DX перед 16-битным делением. Таблица 3.12. Размеры операндов и результатов Операция Первый операнд Второй операнд Результат СЛОЖЕНИЕ 8 (слагаемое) 8 (слагаемое) 8 (сумма) 16 (слагаемое) 16 (слагаемое) 16 (сумма) ВЫЧИТАНИЕ 8 (уменьшаемое) 8 (вычитаемое) 8 (разность) 16 (уменьшаемое) 16 (вычитаемое) 16 (разность) УМНОЖЕНИЕ 8 (множимое) 8 (множитель) 16(произведение) 16 (множимое) 16 (множитель) 32 (произведение) ДЕЛЕНИЕ 16 (делимое) 8 (делитель) 8 (частное) 8 (остаток) 32 (делимое) 16 (делитель) 16 (частное) 16 (остаток) Сброс старшей половины делимого двойной длины хорошо действует для беззнакового деления, а как быть со знаковым делением? Преобразование 8-битного числа -2 (1111 1110) в 16-битное (1111 1111 1111 1110) связано с устаноЕКой 8 старших бит в 1, а преобразование 8-битного числа +3 (0000 ООН) в 16-битное (0000 0000 0000 ООП) - с установкой 8 старших бит в 0. Правило преобразования довольно простое: нужно продублировать левый бит (иногда называемый знаковым) 8-битного числа в каждом разряде старшей половины 16-битного числа. Напомним, что "удлинение" чисел путем дублирования знакового бита называется расширением со знаком. Для этой операции предусмотрены специальные команды. Сначала они назывались SEX (Sign Extend), а затем их переименовали в более умеренные по звучанию команды CBW (преобразовать байт в слово) и CWD (преобразовать слово в двойное слово). Команда CBW расширяет знаковый бит регистра AL во все биты регистра АН, а команда CWD расширяет знаковый бит регистра АХ во все биты регистра DX. На рис. 3.13 показано, как осуществить деление 8- и 16-битных операндов. 58
Знаковое Беззнаковое 8 бит на 8 бит MOVAL, Делитель C8W IDIV АН, Делимое MOVAL, Делитель MOVAK0 DIVAH, Делимое 16 бит на 16 бит MOV АХ. Делитель СЮ OIVAX, Делимое МО VAX, Делитель MOVDX.0 DIV АХ, Делимое Рис. 3.13. Выполнение операции деления с операндом одинаковой длины Десятичная арифметика. До сих пор мы рассматривали арифметические операции над двоичными числами. Компьютеры работают с двоичными числами, но для людей более привычны десятичные. Мы живем в "десятичном мире", и если бы всевышний захотел, чтобы мы работали с двоичными числами, мы бы рождались всего с двумя пальцами. Поэтому первая проблема, с которой мы сталкиваемся при общении с компьютером, заключается в преобразовании входных чисел на язык компьютеров и в обратном преобразовании результатов. Конечно, на эти преобразования расходуется время. Но гораздо хуже то, что компьютер решает задачи не так, как мы, а это может приводить к удивительным результатам. Например, нас привело бы в замешательство то, что показания компьютеризованного спидометра в автомобиле сбрасывались бы после 131 072 миль (а не 99 999) только потому, что 131 072 является степенью числа два. Почему же большинство компьютеров "мыслят по двоичным законам"? Только потому, что они работают всего с двумя уровнями напряжения (0 и 1) и должны представлять числа в двоичной системе счисления. Конечно, нулями и единицами можно закодировать каждую десятичную цифру в отдельности. Например, вместо представления десятичного числа 37 его двоичным эквивалентом 0010 0101 можно взять двоичные коды 3 (ООП) и 7 (0111), что дает представление ООП 0111. Такое двоичное изображение десятичных цифр называется двоично-десятичным кодированием (BCD). В табл. 3.13 показано кодирование всех десятичных цифр. Примене- Таблица 3.13. BCD-кодирование десятичных цифр Цифра 0123456 *7 89 Код 0000 0001 0010 ООП 0100 0101 ОНО 0111 1000 1001 59
ние в компьютерах двоичных чисел вместо двоично-десятичных объясняется тем, что двоичное представление компактнее. Например, число 125 можно представить в 8 битах как двоичное число 01111101, но для BCD-кода потребуется 12 бит - 0001 0010 0101. Как же выполнять арифметические операции с BCD-числами? Можно ли их складывать, вычитать, умножать и делить? Для этого в системе команд компьютера наряду (или вместо) с командами двоичных операций потребовалось бы ввести команды BCD-сложения, BCD-вычитания, BCD- умножения и BCD-деления. Но можно как второй вариант применить команды двоичной арифметики к BCD-числам, заранее зная о неправильном результате, а затем выполнить специальную команду коррекции, которая сформирует правильный BCD-результат. Именно такой вариант выбран в процессоре 80286. Рассмотрим сложение BCD-чисел 23 и 14 с помощью команды 8-битного двоичного сложения. Будет получено: 0010 0011 =23 *0001 0100 = 1 4 00110111 =37 Здесь двоичное сложение дает правильный BCD-результат! Коррекции не требуется. Попробуем теперь сложить 29 и 14: 00101001 =29 *0001 0100 = 1 4 00111101 =3? Ответ неверен, так как код 1101 не соответствует никакой десятичной цифре. В четырех битах можно представить 16 различных цифр, но десятичных цифр всего десять. Поэтому сложение двух цифр, когда сумма превышает 9, ведет в запрещенный диапазон и дает неправильный результат. Коррекция заключается в том, чтобы прибавить 6 к сумме в тех разрядах, где получена запрещенная комбинация, компенсируя этим 6 запрещенных комбинаций. В предыдущем примере сумма корректируется так: 00111101 =3? * ОНО =06 0100 0011 =43 Получен правильный результат 43. Здесь переход в запрещенный диапазон обнаруживается очень просто. Более сложная ситуация возникает, когда сумма "проскакивает" запрещенный диапазон и становится допустимой цифрой. Рассмотрим, например, сложение BCD-чисел 29 и 18: 0010 1001 =29 *0001 1000 =1 8 0100 0001 =41 60
Результат оказался неверным, так как младшая цифра суммы "проскочила" запрещенный диапазон, поэтому ее нужно скорректировать, прибавляя 6. Однако необходимость такой коррекции по самому результату установить невозможно. Признаком "проскакивания" цифрой запрещенного диапазона служит перенос из соответствующего бита (разряда). В приведенном примере им будет перенос из младшего (десятичного) разряда в старший. Следовательно, результат можно скорректировать, если знать переносы из десятичных разрядов. Флажок переноса CF показывает, что при сложении возник перенос из старшего бита (и, следовательно, из старшего десятичного разряда). Флажок вспомогательного переноса AF предназначен только для регистрации переноса из младшего десятичного разряда, зная который можно осуществить BCD-коррекцию. В приведенном выше примере после двоичного сложения CF = 0 и AF = 1. Арифметические операции можно выполнять и над многоразрядными BCD-числами. Покажем сложение чисел 2889 и 3714, которое реализуется двумя операциями сложения и коррекции. 1. Суммируются младшие пары цифр: 10001001 =89 *0001 0100 =14 10011101 =9? CF = 0,AF = 0 2. Выполняется коррекция: 1001 1101 =9? ОНО = коррекция 1010 ООП =?3 ОНО = коррекция 00000011 =03 CF=1,AF = 0 3. Суммируются старшие пары цифр с учетом флажка CF: 1 (последнее значение CF) 0010 1000 =28 *00110111 =37 0110 0000 =60 CF = 0,AF=1 4. Производится коррекция: ,0110 0000 =60 ОНО = коррекция 0110 0110 =66 5. Окончательный результат равен 0110 0110 0000 0011 = 6603 61
Десятичную коррекцию осуществляет команда DAA (десятичная коррекция для сложения), в которой предполагается, что сумма находится в регистре AL. С учетом содержимого регистра AL и состояний флажков АР и CF команда DAA определяет необходимость коррекции и реализует ее для регистра AL. Аналогичная команда DAS (десятичная коррекция для вычитания) корректирует результат после операции вычитания. Для умножения применить коррекцию невозможно, так как в BCD-результате "замешаны" перекрестные члены произведения. По аналогичной причине невозможно скорректировать результат деления. Следовательно, при необходимости умножения и деления десятичных чисел потребуется перейти к другому представлению десятичных чисел, на котором мы и остановимся. Рассмотренный BCD-формат точнее назвать упакованным десятичным форматом, так как в байте упакованы две цифры. В неупакованном формате байт содержит всего одну десятичную цифру. Она находится в четырех младших битах, а старшие биты не влияют на значение цифры. Примером неупакованного десятичного формата служит код ASCII, в котором символы представляются семью битами. Кодирование цифр показано в табл. 3.14. Здесь четыре старших бита содержат ООП, но эта комбинация на значение цифры не влияет. Таблица 3.14. Представление цифр в коде ASCII Цифра Код ASCII Цифра Код ASCII 0 ООП 0000 5 ООН 0101 1 ООП 0001 6 ООП ОНО 2 ООН 0010 7 ООП 0111 3 ООН ООН 8 ООН 1000 4 ООН 0100 9 ООН 1001 Результат двоичного сложения и вычитания ASCII-чисел можно скорректировать аналогично BCD-коррекции, причем корректируется только младшая цифра. Команды, осуществляющие коррекцию, называются командами ASCII-коррекции: AAA (ASCII-коррекция для сложения), AAS (ASCII-коррекция для вычитания), ААМ (ASCII-коррекция для умножения) и AAD (ASCII-коррекция для деления). Как пример ASCII-умножения, рассмотрим умножение числа 9 на 4. Предположим, что число 9 (0000 1001) находится в регистре BL, а число 4 (0000 0100) - в регистре AL. Команда беззнакового двоичного умножения с множителем BL (т.е. команда MUL AL, BL) образует в регистре АХ 16-бит-* ное произведение, равное 36 (0000 0000 0010 0100). Команда коррекции 62
ААМ должна "разложить" результат на 3 (0000 ООП) в регистре АН и на 6 (0000 ОНО) в регистре AL. Для этого нужно просто разделить содержимое AL на десять и поместить частное в регистр АН, а остаток в AL. Оказывается, не случайно команда ААМ имеет длину два байта (ведь было бы достаточно одного байта), причем второй байт - это двоичное представление десяти (1010). По существу, команда ААМ является разновидностью команд деления (хотя она и не помещает частное и остаток в те же регистры, что и команды DIV и IDIV), в которой второй байт команды содержит делитель. Мы не удивимся, если изменение второго байта с 10 (0000 1010) на 7 (0000 0111) приведет к команде "деления на 7", хотя фирма Intel этого не обещает. Но можно предположить также, что при размещении во втором байте числа 16 (0001 0000) реализуется преобразование упакованного BCD-числа из регистра AL в неупакованное число в регистрах АН и AL. В рассмотренном примере операнды 0000 1001 и 0000 0100 были неупакованными числами с нулями в четырех старших битах. Если бы в них были не нули, при умножении появились бы перекрестные члены, которые скрыли бы нужный результат 0010 0100 (именно такие члены делают невозможной коррекцию BCD-умножения). Поэтому перед умножением неупакованных чисел нужно сбросить четыре старших бита каждого операнда, если в исходном состоянии они не содержат нулей. Для сброса выбираемых бит в байте удобно воспользоваться командой AND (см. далее). Мы рассмотрели умножение одноразрядных неупакованных десятичных чисел, а теперь попробуем умножить многоразрядное число на одну цифру, например 539 на 6. В школе нас учили умножать следующим образом: девять, умноженное на 6, дает 54. Записываем 4 и переносим 5 ("четыре пишем, пять в уме"). Три умножаем на 6, получаем 18, прибавляем 5 и имеем 23. Записываем 3 и переносим 2. Пять умножаем на 6, получаем 30, прибавляем 2 и окончательно записываем 32. Процесс умножения выглядит примерно так: 2 5 (переносы) 3234 Теперь посмотрим, как эту задачу решает процессор 80286. Предположим, что неупакованное число 539 хранится в переменных оЗ, а2 и ol, а число 6 - в переменной Ь. Будем полагать, что в старших четырех битах оЗ, а2, al и Ъ находятся нули. Мы хотим умножить значения аЗ, а2, ol на значение переменной Ъ и поместить результат в переменные с4, сЗ, с2, cl:
аЗ а2 al Ь_ c4 сЗ c2 cl Программа такого умножения состоит из следующих шагов: 1. al*b — > АХ ; Девять на шесть равно 54 2- ААМ ; (пять в АН, четыре в AL) 3. AL — -> cl ; Записать 4 4. АН — -> с2 ; и перенести 5 5. а2*Ь — > АХ ; Три на шесть равно 18 6. ААМ ; (единица в АН, восемь в AL) 7. AL + с2 —> AL ; Плюс пять из переноса дает 23 8. AAA ; (два в АН, три в AL) 9. AL - -> с2 ; Записать 3 10. АН - -> сЗ ; и перенести 2 11. аЗ*Ь — > АХ ; Пять на шесть равно 30 12. .ААМ ; (три в АН, нуль в AL) 13. AL + с 3 — > AL ; Плюс два из переноса 14. AAA ; дает 32 15. AL - -> сЗ ; Записать 32 16. АН - -> с4 Отметим в программе наличие команд сложения и соответствующих команд коррекции AAA. Рассмотрим одну из команд AAA подробнее. Когда команда AAA в строке 8 превращает неверное число в регистре AL (0000 1101) в правильное 3 (0000 ООП), возникает перенос из младшей цифры регистра AL. Этот перенос не подается в старшую цифру регистра AL, а направляется в младшую цифру регистра АН, корректируя содержимое АН с 1 (0000 0001) на 2 (0000 0010). Следовательно, команда AAA осуществляет коррекцию содержимого не только регистра AL, а обоих регистров АН и AL. Такое действие команды AAA было бы ненужным, если бы она применялась только для сложения, а не умножения. Сложение Первое слагаемое: а(п) а(п - 1) а(п - 2)... а(3) а(2) а(1) Второе слагаемое: Ь(п) Ь(п - 1) Ь(п - 2)... Ь(3) Ь(2) Ь( 1) Сумма: с(п + 1) с(п) с(п - 1)с(л- 2)... с(3) с(2) с(1) Сбросить флажок переноса CF выполнить следующие шаги для / от 1 до п: Передать а(7) в AL Прибавить с переносом Ь(\) к AL Рис. 3.14. Аоифметикамногозарядных неупакованных десятичных чисел 64
Скорректировать сложение из AL в АН, AL Передать AL в c(i) Передать АН в с(п + 1) Вычитание Уменьшаемое: а(п) а(п - 1J а(п - 2)... а(3) а(2) а(1) Вычитаемое: bfn) b(n - 1) bfn - 2)... b(3) b(2) b(1) Разность: с(п + 1) с(п) с(п - 1) с(п - 2)... с(3) с(2) с(1) Сбросить флажок переноса CF Выполнить следующие шаги для / от 1 до п : Передать a(i) в AL Вычесть с заёмом b(i) из AL Скорректировать вычитание из AL в АН, AL Передать AL в c(i) Передать АНвс(п + 1) Умножение Множимое: а(п) а(п - 1) а(п - 2)... а(3) а(2) а(1) Множитель: Ь Произведение: с(л + 1) с(п) с(п - 1) с(п - 2)... с(3) с(2) с(1) Сбросить старшие четыре бита b Сбросить с(1) Выполнить следующие шаги для / от 1 до л: Сбросить старшие четыре бита а(/), передать в AL Умножить AL на b Скорректировать умножение из AL в АН, AL Прибавить с(1) к AL Скорректировать сложение из AL в АН, AL Передать AL в с(7) Передать АН в с(/ +1) Деление Делимое: а(п) а(п - 1) а(п - 2)... а(3) а(2) а(1) Делитель: Ь Частное: с(п) с(п - 1) с (п - 2) ... с(3) с(2; ф) Сбросить старшие четыре бита b Сбросить АН Рис. 3.14. (Продолжение) 3 Заказ 6126 65
Выполнить следующие шаги для / от 1 до л: Сбросить старшие четыре бита а(7), передать в AL Скорректировать деление из АН, AL в AL Разделить AL на Ь с образованием остатка в АН Передать AL в c(i) Рис. 3.14. (Окончание) Более компактный алгоритм с циклом для многоразрядного неупакованного умножения показан на рис. 3.14. Хотя мы рассмотрели только одноразрядные множители, обобщение алгоритма на многоразрядные множители не вызывает серьезных трудностей. Обратимся теперь к делению неупакованных чисел, например разделим 42 на 6. Будем считать, что 42 находится в регистре АХ (0000 0100 - в АН, 0000 0010 в AL), а 6 (0000 ОНО) - в BL. Неупакованное представление одноразрядного числа, например б, является и его двоичным представлением. Следовательно, делимое 42 нужно преобразовать в двоичное число. Для этого содержимое АН умножается на 10 и прибавляется к содержимому регистра AL. Тогда двоичное деление содержимого регистра AL (двоичного числа 42) на содержимое регистра BL (6) образует в AL двоичное представление 7, которое совпадает с неупакованным представлением 7; на этом неупакованное деление закончено. Подчеркнем в рассмотренном примере три важных момента. Во-первых, коррекция деления (AAD) заключается в умножении содержимогр регистра АН на 10 и прибавлении к содержимому регистра AL (не случайно второй байт команды AAD содержит число 10). Во-вторых, коррекция деления предшествует операции деления, а в сложении, вычитании и умножении коррекция производится после соответствующей арифметической операции. Другими словами, коррекция сложения, вычитания и умножения исправляет "плохой" результат, а коррекция деления предотвращает получение "плохого" результата. В-третьих, делимое и делитель должны иметь нули в старших четырех битах. Это же требование относится и к умножению, но не обязательно для сложения и вычитания. Многоразрядное делимое можно разделить на одноразрядный делитель примерно так же, как это было сделано для умножения (см. алгоритм на рис. 3.14). К сожалению, этот метод не допускает обобщения на многоразрядный делитель. Можно рекомендовать алгоритм деления, в котором делается предположение о частном, затем с помощью умножения и вычитания неупакованных чисел проверяется правильность предположения и оно последовательно уточняется. Примерно так мы осуществляем деление длинных чисел на бумаге. Однако имеются и более совершенные алгоритмы деления, бб
3.4. ЛОГИЧЕСКИЙ команды Логические команды процессора 80286 состоят из булевых команд и команд сдвигов /циклических сдвигов (см. табл. 3.15). Таблица 3.15. Логические команды Мнемоника команды Описание команды AND ~~ ПРИЕМНИК Л ИСТОЧНИК - ПРИЕМНИК TEST ПРИЕМНИК Л ИСТОЧНИК - ? OR ПРИЕМНИК V ИСТОЧНИК - ПРИЕМНИК XOR ПРИЕМНИК Ф ИСТОЧНИК - ПРИЕМНИК NOT ПРИЕМНИК-ПРИЕМНИК SHL (сдвиг логический влево) CF - ПРИЕМНИК *• 0 SHR (сдвиг логический вправо) 0 - ПРИЕМНИК - CF SAL (сдвиг арифметический влево) Аналогично SHL SAR (сдвиг арифметический вправо) Знак - ПРИЕМНИК - CF ROL (циклический сдвиг влево) ROR (циклический сдвиг вправо) RCL (циклический сдвиг влево через перенос) RCR (циклический сдвиг вправо через перенос) Булевы команды. К булевым относятся команды NOT, AND, OR, XOR и TEST. Примеры их были приведены в табл. 3.7 и 3.8. Команды AND, OR и XOR выполняют логическую функцию над соответствующими битами источника и приемника, помещая результат в йриемник. Команда NOT имеет всего один операнд; она выполняет инверсию каждого его бита и помещает результат в то же место. Логические функции, реализуемые рассматриваемыми командами, определены в табл. 3.16. 0 Таблица 3.16. Определение логических функций Однооперандная Бит источника NOT 0 1 1 0 Двухоперандные Бит источника Бит приемника AND OR XOR 0 0 ООО 0 1 0 1 1 1 0 0 11 1 1 110 67
Функцию AND (И) удобно применять для сброса (маскирования) указанных разрядов в числе; один операнд определяет разряды, а второй - само число. Например, можно сбросить старшие четыре бита в 8-битном числе, объединяя его по И с набором 0000 1111. (Напомним, что сброс этих бит требуется до выполнения десятичного умножения и деления.) Функции OR (ИЛИ) и XOR (исключающее ИЛИ) применяются для установки в 1 и инвертирования указанных разрядов в числе. Например, для установки в 1 старшего бита 8-битного числа следует объединить его по ИЛИ с набором 1000 0000, а для инвертирования средних четырех бит 8-битного числа нужно объединить его по исключающему ИЛИ с набором ООП 1100. Команда XOR позволяет также сбросить содержимое регистра в нуль (регистр должен быть и источником, и приемником). Функция NOT (НЕ) инвертирует все биты числа; она эквивалентна команде XOR с операндом-источником 11111111. Команда TEST объединяет возможности команд AND и СМР. Как команда AND, она выполняет объединение по И соответствующих бит операндов; как команда СМР, она сохраняет только состояния флажков, а не результат. Она удобна для проверки того, есть ли в указанных разрядах числа хотя бы одна 1. Здесь один операнд определяет разряды, а второй - число. Если (уничтожаемый) результат не равен 0, о чем сигнализирует флажок ZF = 0, то, по крайней мере, один из указанных разрядов содержит 1. Например, для проверки наличия 1 в четырех младших битах регистра BL следует поместить 0000 1111 в регистр ВН, выполнить команду TEST BL, ВН, а затем воспользоваться командой условного перехода, которая передает управление, если ZF = 0. Конечно, вместо команды TEST можно применить команду AND, но при этом операнд-приемник будет уничтожен. Команды сдвигов. Команды сдвигов являются эффективным средством увеличения и уменьшения числа вдвое (меньше байт и тактов, чем в умножении и делении). Для удвоения беззнакового числа нужно сдвинуть все биты на один разряд влево, а в освобождающийся правый бит поместить 0. Если выдвинутый слева бит передать во флажок CF, то можно зафиксировать выход за диапазон, проверив условие CF = 1. Например, удвоение числа 65 (0100 0001) с помощью сдвига влево дает 130 (1000 0010) и CF = 0 (в диапазоне), а сдвиг влево числа 130 (1000 0010) дает 4 (0000 0100) и CF = 1 (вне диапазона). Аналогично уменьшение беззнакового числа вдвое осуществляется сдвигом всех бит на один разряд вправо, в освобождающийся бит помещается 0, а выдвигаемый справа бит передается во флажок CF. В этом случае CF = 1 показывает, что число было нечетным. Например, сдвиг вправо числа 9 (0000 1001) дает 4 (0000 0100) и CF = 1. С беззнаковыми числами работают команды SHL (сдвиг влево) и SHR (сдвиг вправо), а команды SAL (арифметический сдвиг влево) и SAR (арифметический сдвиг вправо) предназначены для знаковых чисел. 68
Различие между уменьшением вдвое знакового числа (SAR) и беззнакового числа (SHR) заключается в том, что в первом случае левый бит (знак) изменяться не должен. Например, уменьшение вдвое числа +6 (0000 ОНО) должно дать +3 (0000 ООП), а числа -120 (1000 1000) - дать результат -60 (1100 0100). Следовательно, команда SAR сдвигает все биты на один разряд вправо, но сохраняет знаковый бит неизменным; команда же SHR помещает в знаковый бит 0. Отметим, что результатом команды SAR с операндом +5 (0000 0101) будет число +2 (0000 0010), а с операндом -5 (1111 1011) - число -3 (1111 1101). Сдвиг вправо нечетного ^исла всегда дает результат, который меньше половины числа (-3 < -2*'2). К сожалению, это число не совпадает с результатом команды DIV; деление числа -5 на 2 с использованием DIV дает-2. Различий между удвоением знакового и беззнакового чисел нет. Поэтому мнемоники SHL и SAL относятся к одной и той же команде. Команды циклического сдвига позволяют переставить биты в числе. Команды ROL (циклический сдвиг влево) и ROR (циклический сдвиг вправо) обеспечивают циклический сдвиг влево и вправо: выдвигающийся бит подается в освобождающийся бит. В команде RCL (циклический сдвиг влево через перенос) и RCR (циклический сдвиг вправо через перенос) в кольцо сдвига включается флажок CF: выдвигающийся бит подается во флажок CF, а состояние флажка CF передается в освобождающийся бит. Операнд команд сдвигов может находиться в памяти или в регистре; длина операнда равна 8 или 16 бит. Сдвиг осуществляется на предопределенное число бит (фиксированный сдвиг) или на любое число бит (переменный сдвиг). В первом случае число сдвигов определяется в команде, а во втором - содержимым регистра CL, называемого счетчиком (вот еще один пример специализации одного из регистров общего назначения). Примеры команд сдвигов показаны в табл. 3.17. (В микропроцессоре 8086 фиксированный сдвиг осуществляется только на один бит.) Таблица 3.17. Примеры команд сдвигов Фиксированный сдвиг Операнд Регистр Память Слово КОПВХ.13 КОП MEMW, 1 Байт КОП DL,1 КОП МЕМВ, 7 Переменный сдвиг Операнд Регистр Память Слово КОПАХ,СЬ КОП MEMW, CL Байт КОП BL, CL КОП МЕМВ, CL 69
3.5. ЦЕПОЧЕЧНЫЕ КОМАНДЫ Под цепочкой понимается последовательность байт или слов в памяти, а цепочечной операцией называется операция, которая выполняется над каждым элементом цепочки. Например, цепочечная передача производит пересылку целой цепочки из одной области памяти в другую. Так как цепочечная операция включает в себя повторения, она может выполняться сравнительно долго. Процессор 80286 имеет набор команд, которые сокращают время выполнения цепочечных операций. Сокращение времени достигается благодаря мощному набору примитивных команд, ускоряющих обработку каждого элемента цепочки, и устранению служебных действий, которые обычно требуются между обработкой последовательных элементов. Примитивные цепочечные команды приведены в табл. 3.18. Таблица 3.18. Цепочечные примитивы Мнемоника команды Описание команды MOVS (передать) ИСТОЧНИК - ПРИЕМНИК Модифицировать SI, DI CMPS (сравнить) ИСТОЧНИК - ПРИЕМНИК - ? Модифицировать SI, DI SCAS (сканировать) AL-ПРИЕМНИК-? Модифицировать DI INS (ввести) ВХОДНОЙ ПОРТ - ПРИЕМНИК Модифицировать DI OUTS (вывести) ИСТОЧНИК - ВЫХОДНОЙ ПОРТ Модифицировать SI LODS (загрузить) ИСТОЧНИК - AL Модифицировать SI STOS (запомнить) AL - ПРИЕМНИК Модифицировать DI В операциях над словами вместо регистра AL используется регистр АХ. Элементарные цепочечные команды. Чтобы показать ускорение обработки цепочек с помощью цепочечных команд, рассмотрим, как передается последовательность байт. Необходимо как-то указать, где находятся байты и где они должны быть. Воспользуемся для этой цели регистрами SI (индекс источника) и DI (индекс приемника). В регистр SI поместим значе- 70
ние смещения первого байта последовательности в текущем сегменте данных, а в регистр DI - значение смещения, по которому этот байт должен быть передан. Число пересылаемых байт удобнее всего загрузить в регистр-счетчик СХ. Если он первоначально содержит нуль, пересылать нечего. Передача цепочки включает в себя следующие действия: 1. Если регистр СХ содержит нуль, операция закончена. 2. Считать байт, смещение которого находится в регистре SI. 3. Запомнить этот байт в ячейке, смещение которой находится в регистре DI. 4. Произвести инкремент содержимого регистра SI на 1. 5. Произвести инкремент содержимого регистра DI на 1. 6. Произвести декремент содержимого регистра СХ на 1. 7. Вернуться к шагу 1 и повторить операцию. Фактическая передача каждого байта реализуется на этапах 2 и 3, а этапы 1, 4 - 7 являются служебными. Передачу каждого байта можно ускорить, если иметь примитивную команду, которая передает байт, смещение которого находится в регистре SI, в байт, смещение которого содержится в DI. Далее, если эта примитивная команда осуществляет также инкремент содержимого регистров SI и DI, можно устранить часть служебных действий. При наличии такой команды пересылка цепочки упрощается: 1. Если регистр СХ содержит нуль, операция закончена. 2. Выполнить операцию "примитив передачи". 3. Произвести декремент содержимого регистра СХ на 1. 4. Вернуться к шагу 1 и повторить операцию. Этапы 1, 3 и 4 можно устранить, если примитивная команда будет выполнять действие "проверка, декремент и повторение" над регистром СХ. В результате остается единственный шаг, включающий в себя "примитив передачи". Теперь пересылка цепочки реализуется так: 1. "Усилить" сопровождающий примитив. 1а. Выполнить "примитив передачи". Команда MOVS (передать элемент цепочки) в процессоре 80286 и является рассмотренным примитивом. Более того, любой цепочечный примитив можно "усилить", указав перед ним специальный однобайтный префикс, называемый префиксом повторения. Комбинация префикса повторения и примитива MOVS образует двухбайтную команду и записывается как REP MOVS. Если область, в которой размещается исходная цепочка, перекрывается с областью, в которую она пересылается, возникает сложность. Рассмотрим, например, передачу пяти байт с начальным смещением 100 в пять байт, имеющих начальное смещение 102 (рис. 3.15). Байты со смещениями 100 и 101 успешно копируются в байты со смещениями 102 и 103. Но когда 71
подходит очередь копировать байт со смещением 102 в байт со смещением 104, возникает проблема: байт в смещении 102 является не исходным байтом, а байтом, переданным из смещения 100. Следовательно, байт из смещения 100 будет копироваться еще раз, теперь в смещении 104. В конце операции он будет скопирован и в смещении 106. Аналогично байт из 101 будет передан в 103 и 105. Задача решается, если пересылать байты в обратном порядке, т.е. первым передается байт из смещения 104, затем из 103 и т.д. Но если имеется перекрытие в другом направлении (байты из смещений 100 - 104 передаются в байты смещений 98 - 102), обратный порядок не подходит и нужно передавать в прямом направлении. Имейте в виду, что "проклятие для одного человека оказывается благом для другого". Проклятие перекрывающихся пересылок цепочек становится очень удобным при необходимости заполнения области памяти одним и тем же набором (тщательно проанализируйте предыдущий пример). В процессоре 80286 имеется флажок DF направления, который определяет направление обработки цепочек. Если DF = 0, считается, что цепочки размещены в прямом направлении (к большим адресам), начиная со смещений в регистрах SI и DI. Если же DF = 1, цепочки размещены в обратном направлении, поэтому в цепочечных примитивах производится декремент содержимого регистров SI и DI, а не инкремент. Следовательно, в случае перекрывающейся передачи, когда байты пересылаются по большим смещениям и требуется обратная передача, флажок DF необходимо установить в 1. В зависимости от состояния флажка DF в регистрах SI и DI содержатся либо меньшие смещения (DF = 0), либо большие смещения (DF = 1) цепочек. Команды задания состояния флажка DF рассматриваются далее. Чтобы облегчить передачу цепочек из одного сегмента в другой, было бы удобно, если в регистрах SI и DI находились бы смещения, относящиеся к разным сегментам. Мы уже говорили, что регистр SI содержит сме- после щение в текущем сегменте данных, jho не указали, к какому сегменту в регистре DI. • Было бы хорошо, если бы примитивные цепочечные команды считали смещение в регистре DI относящимся к текущему дополнительному сегменту. В процессоре 80286 так и сделано. Поэтому для пересылки цепочки из одного сегмента в другой следует Рис. 3.15. Перекрывающаяся пересылка До 100 101 102 103 104 Ю5: 106! 100! I ♦ относится смещение 101 '102 'юз "104 405 М06 72
загрузить в сегментные регистры DS и ES начальные адреса соответствующих сегментов, а в индексные регистры SI и DL- смещения в этих сегментах. Конечно, пересылка цепочки в пределах одного сегмента реализуется при загрузке в DS и ES одного и того же значения. Передачу цепочки из сегмента, отличающегося от адресуемого DS, можно осуществить, указав префикс замены сегмента (этот префикс воздействует только на операнд-источник). Некоторые цепочечные операции эффективнее выполнять над словами, а не над байтами. Например, пересылка производится гораздо быстрее, если передаваемыми элементами будут слова. Чтобы обрабатывать цепочки слов, в каждой примитивной команде имеется поле, которое задает операции с байтами или словами. Передача слов отличается от передачи байт тем, что инкремент (декремент, если DF = 1) содержимого регистров SI и DI равен 2, а не 1. Однако декремент содержимого регистра СХ всегда равен 1, поэтому при работе со словами регистра СХ инициализируется на число слов, а не байт. Вот как выглядят повторяющиеся примитивы пересылок слов и байт: MOV SI,OFFSET MEMW1 ; Передать в SI смещение MEMW1 MOV DI,OFFSET MEMW2 ; Передать в DI смешение MEMN2 REP MOVS MEMW2,MEMW1 ; Повторяющаяся передача слов MOV SI,OFFSET МЕМВ1 ; Передать в SI смещение МЕМВ1 MOV DI,OFFSET МЕМВ2 ; Передать в DI смещение МЕМВ2 REP MOVS МЕМВ2,МЕМВ1 ; Повторяющаяся передача байт Отметим, что примитив пересылки производит передачу содержимого ячейки по адресу памяти, смещение которого находится в регистре SI, ячейку по адресу, смещение которого находится в регистре DI. Казалось бы, что в команде не нужно определять никаких операндов, так как у нее нет выбора, какие элементы передавать и куда. Однако ассемблер должен знать, что передавать - байты или слова; он узнает об этом по заданию операндов (они должны быть одного и того же типа). Кроме того, как и в команде XLAT, Ассемблер контролирует нахождение операндов в доступных сегментах. Рассмотрим теперь другую цепочечную операцию: сканирование последовательности байт в поисках байта, отличающегося от заданного. Примером может служить поиск первого ненулевого элемента в таблице. Вновь предположим, что регистр SI содержит смещение последовательности, а регистр СХ - число байт в ней. Разместим в регистре AL байт, который нужно пропускать. Сканирование состоит из следующих действий: 1. Если регистр СХ содержит нуль, операция закончена. 2. Считать байт, смещение которого находится в регистре SI. 73
3. Сравнить его с байтом из регистра AL (сравнение предполагает вычитание и установку флажков, в частности, флажка ZF). 4. Произвести инкремент (декремент, если DF = 1) содержимого регистра SI на 1. 5. Произвести декремент содержимого регистра СХ на 1. 6. Если ZF ■ 1, то два байта одинаковы; вернуться к шагу 1 и повторять. Шаги 2,3 и 4 выполняются примитивом сканирования SCAS (сканировать элемент цепочки). Шаги 1, 5 и 6 реализуются, если примитив "усилен" префиксом повторения. Сканирование слов аналогично сканированию байт, но вместо регистра AL используется АХ и инкремент (декремент) производится на 2, а не на 1. Отметим, что префикс повторения в примитиве сканирования действует иначе, чем в примитиве пересылки; при сканировании перед повторением проверяется состояние флажка ZF. В общем, префикс повторения проверяет флажок ZF, когда примитивная цепочечная операция может его модифицировать. (Команда MOVS никогда не воздействует на флажок ZF, а команда SCAS устанавливает или сбрасывает ZF в зависимости от того, равны байты или нет.) Еще одна цепочечная операция заключается в сканировании последовательности байт с целью поиска конкретного значения. Если, например, байты содержат символы кода ASCII, эта операция находит первое появление конкретного символа в сообщении. Для этого перед примитивной командой сканирования указывается префикс повторения, как и в предыдущей операции сканирования, но теперь условием повторения становится ZF ■ 0. Так как проверку состояния флажка ZF определяет префикс повторения, он должен указать, какое состояние флажка ZF вызывает повторение; для этого в префиксе выделен специальный бит. Ассемблерные мнемоники таких форм повторения имеют вид REPZ (повторять, пока флажок ZF установлен) и REPNZ (повторять, пока флажок ZF сброшен). Альтернативными мнемониками являются REPE (повторять, пока равны) и REPNE (повторять, пока не равны); они более четко показывают условие, при котором осуществляется зацикливание. Когда же префикс повторения не проверяет флажок ZF, он записывается просто как REP (синоним REPZ). Следующая цепочечная операция сравнивает две последовательности байт для определения того, какая из них оказывается первой. Если, в частности, байты содержат символы в коде ASCII, эта операция проверяет последовательности на лексико-графический порядок (лексико-графиче- ский - это просто другое название "алфавитный", но с учетом не только алфавитных символов). Вновь предположим, что смещения двух последовательностей находятся в регистрах SI и DI, а число сравниваемых байт 74
(размер более короткой последовательности) - в регистре СХ. Цепочечное сравнение реализуется такими действиями: 1. Если регистр СХ содержит нуль, операция закончена. 2. Считать байт, смещение которого находится в регистре SI. 3. Сравнить его с байтом, имеющим смещение в регистре DI. 4. Произвести инкремент (декремент, если DF = 1) содержимого регистра SI на 1. 5. Произвести инкремент (декремент, если DF = 1) содержимого регистра DI на 1. 6. Произвести декремент содержимого регистра СХ на 1. 7. Если ZF = 1, то два байта одинаковы, поэтому вернуться к шагу 1 и повторить орерацию. Шаги 2,3,4 и 5 реализуются примитивом сравнения CMPS (сравнивать элементы цепочек), а остальные шаги выполняются, если к команде CMPS добавлен префикс повторения REPE. Сравнение слов аналогично сравнению байт, но инкремент или декремент содержимого регистров SI и DI производится на 2, а не на 1. ■ Рассмотрим операцию сравнения подробнее. Когда сравниваемые на шаге 3 байты одинаковы, флажок ZF устанавливается в 1 и шаг 7 вызывает зацикливание. Оно прекращается, когда либо байты различаются (и шаг 7 не вызывает повторения), либо достигается конец более короткой цепочки (на шаге 1 осуществляется выход из цикла). После окончания цикла по флажку ZF можно проверить, не достигнут ли конец более короткой цепочки (в этом случае ZF = 1). Можно также проверить флажок переноса CF, чтобы узнать, какая цепочка длиннее (CF = 1 означает, что длиннее цепочка, адресуемая регистром DI). Следующие два цепочечных примитива обеспечивают считывание данных из входного устройства в последовательные ячейки памяти и запись данных из последовательных ячеек памяти в выходное устройство. Они упрощают передачи больших блоков данных между памятью и внешними устройствами; блоковые передачи часто требуются для дисковых накопителей. Примитив ввода INS передает данные из входного порта, определяемого содержимым регистра DX, в байт или слово, смещение которого находится в DI, и производит инкремент (декремент, если DF = 1) содержимого регистра DI на 1 или 2. Аналогично примитив вывода OUTS передает байт или слово, смещение которого находится в регистре SI, в выходной порт, адресуемый регистром DX, и производит инкремент (декремент, если DF = 1) содержимого регистра SI на 1 или 2. Команд INS и OUTS в микропроцессоре 8086 нет. Последние два цепочечных примитива - это LODS (загрузить элемент цепочки) и STOS (запомнить в элементе цепочки). Примитив загрузки передает байт или слово, смещение которого находится в регистре SI, в 75
аккумулятор AL или АХ и производит инкремент (декремент, если DF = 1) содержимого регистра SI на 1 или 2. Примитив запоминания передает байт или слово, содержащееся в регистре AL или АХ, в байт или слово, смещение которого находится в регистре DI, и производит инкремент (декремент, если DF = 1) содержимого регистров DI на 1 или 2. В отличие от предыдущих примитивов LODS и STOS не предназначены для использования с префиксом повторения, а введены для построения более сложных цепочечных операций. Однако примитив запоминания реализует полезную функцию и с префиксом повторения: он заполняет последовательные байты или слова одним и тем же значением. (Эту функцию можно реализовать и с помощью пересылки цепочки, но здесь потребуются две цепочки вместо одной.) Префикс повторения в примитиве загрузки абсолютно бесполезен: он повторно загружает в регистр AL или АХ последовательные байты или слова цепочки, всякий раз разрушая предыдущее загруженное значение. Сложные цепочечные команды. Пять примитивных цепочечных команд осуществляют наиболее часто встречающиеся цепочечные операции. Наверное, было бы бессмысленно разрабатывать примитивные команды для всех возможных операций. Удобнее предусмотреть средства построения эффективных сложных цепочечных команд, возможно, с привлечением некоторых примитивов. Рассмотрим, например, операцию изменения знака последовательности байт, в которой каждый байт представляет собой 8-битное знаковое число. Пусть регистр SI содержит смещение первого байта последовательности, a DI - смещение первого байта той области, в которую помещается последовательность с измененным знаком. Предположим также, что регистр СХ содержит число байт в последовательности. Операция состоит из следующих шагов: 1. Если регистр СХ содержит нуль, операция закончена и остальные шаги пропускаются. 2. Считать байт, смещение которого находится в регистре SI. 3. Произвести инкремент содержимого регистра SI на 1. 4. Изменить знак считанного байта. 5. Запомнить результат в байте, смещение которого содержится в регистре DI. 6. Произвести инкремент содержимого регистра DI на 1. 7. Произвести декремент содержимого регистра СХ на 1. 8. Вернуться к шагу 1, если значение содержимого регистра СХ не равно 0. Как и в предыдущих примерах, удобно иметь примитивную команду, которая реализует шаги 2 - 6. Но такой команды нет! Поэтому придется выполнять указанные шаги с помощью имеющихся команд процессора 80286. Если привлечь цепочечные примитивы, то инкремент содержимого 76
регистров SI и DI выполняется без дополнительных команд. Как видно, шаги 2 и 3 можно реализовать примитивом загрузки, шаг 4 - командой изменения знака, а шаги 5 и 6 - примитивом запоминания. Получаются следующие действия: 1. Если регистр СХ содержит нуль, операция закончена и остальные шаги пропускаются. 2. Выполнить примитив загрузки. 3. Изменить знак байта в регистре AL. 4. Выполнить примитив запоминания. 5. Произвести декремент содержимого регистра СХ на 1. 6. Вернуться к шагу 1, если значение содержимого регистра СХ не равно 0. Ранее шаги 1,5 и 6 осуществлялись посредством "усиления" цепочечного примитива префиксом повторения. Здесь же тело цикла состоит не только из цепочечного примитива, поэтому использовать префикс повторения нельзя. Потребуются менее эффективные команды, которые моделируют сложные действия префикса повторения. Шаг 1 потребует команды условного перехода, вызывающей передачу управления, если регистр СХ содержит нуль. Назначение перехода необходимо определить как можно короче. Поэтому неудивительно, что в процессоре 80286 есть команда JCXZ, которая передает управление, если регистр СХ содержит нуль. Назначение перехода задано в команде одним байтом; в нем находится разность (как знаковое число) между значением смещения назначения и значением смещения команды JCXZ. Желательно также иметь команду, которая производит декремент содержимого регистра СХ и осуществляет переход, если в СХ не содержится нуль. Такая команда тоже имеется и называется LOOP; назначение перехода в команде LOOP задано одним байтом, как и в команде JCXZ. Теперь наш пример превращается в следующие действия: 1. Выполнить команду JCXZ. 2. Выполнить операцию "примитив загрузки". 3. Изменить знак байта в регистре AL. 4. Выполнить операцию "примитив запоминания". 5. Выполнить команду LOOP. Здесь каждый шаг реализуется всего одной командой: JCXZ DONE CYCLE: LODS MEMBYTE1 NEG AL STOS MEMBYTE2 LOOP CYCLE DONE: 77
Команда LOOP осуществляет переход в зависимости от содержимого регистра СХ. Но, как мы видели ранее, иногда желательно повторение цикла с учетом состояния флажка ZF. Соответствующими командами процессора 80286 являются LOOPZ (зациклить, если ZF = 1) и LOOPNZ (зациклить, если ZF * 0). Конечно, обе команды LOOPZ и LOOPNZ перед зацикливанием производят декремент содержимого регистра СХ и проверяют его на нуль. Команды имеют альтернативные мнемоники LOOPE (зациклить, если равны) и LOOPNE (зациклить, если не равны); они более наглядно показывают условие зацикливания. Для примера использования команды LOOPNZ обратимся к изменению знака последовательности байт, но теперь число байт не определено. Известно, однако, что ни один байт в последовательности не содержит нуля, но вся последовательность заканчивается нулевым байтом. Напомним, что команда NEG устанавливает флажок ZF, если байт-операнд содержит нуль. Получаются следующие действия: CYCLE: LODS MEMBYTE1 NEG AL ST1S MEMBYTE2 LOOPNZ CYCLE Отметим, что первая команда JCXZ оказалась ненужной. Подумайте, почему? В заключение рассмотрим пример преобразования чисел от 0 до 15 в код Грея. В этом коде соседние значения отличаются только одним битом; он имеет такой вид: Двоичное число Код Грея 0000 0000 0001 0001 0010 ООН ООН 0010 0100 оно 0101 0100 оно 0101 0111 0111 1000 1111 1001 1110 1010 1100 1011 1101 1100 1001 1101 1011 1110 1010 1111 1000 Предположим, что в текущем сегменте данных со смещением МЕМВ1 находится последовательность байт, содержащих двоичные числа от 0 до 78
15. Регистр СХ содержит некоторое число байт в последовательности. Далее, пусть последовательность из 16 байт в текущем сегменте данных со смещением GRAY представляет собой таблицу преобразования в код Грея. Отметим, что заданные условия идеальны для применения команды XLAT. Разместим преобразованную последовательность в дополнительном сегменте данных со смещением МЕМВ2. Получается следующий фрагмент преобразования: SI,OFFSET MEMBYTE1 MOV DI.OFFSET MEMBYTE2 MOV ВX,GRAY JCXZ DONE LODS MEMBYTE1 XLAT GRAY STOS MEMBYTE2 LOOP CYCLE DONE: Команда XLAT специально разработана с учетом цепочечных циклов. 3.6. КОМАНДЫ БЕЗУСЛОВНОЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ К командам настоящей группы относятся команды переходов, вызовов (подпрограмм) и возвратов (из подпрограмм). Переходы загружают значение в указатель команды IP, нарушая этим последовательное выполнение команд. Вызовы выполняют то же самое, но вначале они запоминают текущее значение содержимого указателя IP в стеке, так что в последующем можно возобновить выполнение программы с этой точки. Возвраты и осуществляют это возобновление; они берут элемент из стека и возвращают его в указатель команды. Вызовы и возвраты - средства организации процедур. Но пока ничего нового в сказанном нами нет. Новым в процессоре 80286 является то, что переходы, вызовы и возвраты бывают двух видов - внутрисегментные и межсегментные. Первые из них передают управление внутри текущего сегмента кода, а вторые - в произвольный сегмент кода (изменяя содержимое регистра CS), который становится текуццш сегментом кода. Очевидно, межсегментные передачи управления могут сделать все, ч?о делают внутрисегментные, и что-то еще. Зачем же потребовались две разновидности команд? Просто потому, что межсегментные передачи управления выполняются дольше (ведь они и делают больше); кроме того, за исключением возвратов, они оказываются длиннее (им нужно больше информации). В примере межсегментного перехода предположим, что текущий сегмент кода начинается в В 0000 и что указатель команды содержит 00А0. Это значит, что следующей выполняется команда по адресу B00A0. 79
Пусть по этому адресу находится команда перехода, которая передает управление ячейке АО 100. Текущий сегмент кода занимает диапазон от В0000 до BFFFF, поэтому переход к ячейке АО 100 должен быть межсегментным. Он должен определять новое значение для регистра CS (возможно, А000) и новое значение для IP (0100). Пример этой команды показан на рис 3.16. Память CS I В о о о .80000 О О А О -ВООАО Рис. 3.16. Пример команды межсегментного перехода }Новый IP >Новый CS В программе межсегментный и внутрисегментный переходы обозначаются мнемоникой JMP; ассемблер может узнать, находится ли назначение в том же сегменте, что и команда перехода, и образовать соответствующую команду. Межсегментный вызое сохраняет в стеке текущее содержимое регистров CS и IP. Межсегментный возврат берет из стека два 16-битных значения и помещает их в регистры IP и CS. В случае внутрисегментного вызова и возврата в стеке сохраняется и восстанавливается только указатель команды IP. Отметим, что все вызовы процедуры должны быть либо межсегментными, либо внутрисегментными, так как возвраты в ароцедуре должны извлекать из стека такое же число байт, какое включает в стек каждый вызов. Поэтому может оказаться, что для вызова процедуры в том же самом сегменте применяется межсегментный вызов просто потому, что в некотором другом сегменте есть еще один вызов (обязательно межсегментный) этой же процедуры. Для обоих типов команд вызова и возврата применяются мнемоники CALL и RET соответственно. Ассемблеру нужно как-то сообщить, какие типы вызовов и возвратов генерировать для конкретной процедуры. 80
С этой целью процедуры ограничиваются операторами PROC и ENDP, например: UPCOUNT PROC NEAR ADD СХ,1 RET UPQOUNT ENDP Поскольку UPCOUNT объявлена процедурой NEAR (близкой), все вызовы ее ассемблируются как внутрисегментные, а все возвраты в ней- так же, как внутрисегментные. Запись FAR (далекая) вместо NEAR заставит все вызовы и возвраты быть межсегментными. Предыдущий пример межсегментного перехода показывает еще и принцип прямой передачи управления. Прямой переход (или вызов) немедленно указывает, куда направляться. Косвенный же переход несколько сложнее; он сообщает, куда обратиться, чтобы узнать, куда направляться. Косвенные передачи управления полезны, когда неизвестно дальнейшее направление и необходимо сначала вычислить назначение. Например, косвенный межсегментный переход или вызов определяет первый из четырех смежных байт в памяти, которые содержат новое значение в регистре IP (два байта) и новое значение в регистре CS (два байта). Эти значения могут быть вычислены в предыдущих командах. Возвраты никогда не сообщают, куда направляться; вместо этого они заставляют вернуться туда, откуда произведен вызов. Поэтому понятие косвенного возврата смысла не имеет. В табл. 3.19 показаны примеры безусловных переходов, вызовов и возвратов. Как мы говорили, ассемблерные операторы прямых внутрисегментных переходов и вызовов выглядят так же, как и межсегментные, и никаких трудностей для ассемблера здесь не возникает. Рассмотрим, однако, косвенную передачу управления, например JMP MEMLOC. Таблица 3.19. Примеры команд безусловной передачи управления Внутрисегментн ые Межсегментн ые Прямые Косвенные Прямые Косвенные Переход JMP LABEL JMP MEMLOC JMP LABEL JMP DWORD PTR MEMLOC Вызов CALL LABEL CALL MEMLOC CALL LABEL CALL DWORD PTR MEMLOC Возврат RET RET Возврат с извлече- RET 6 RET 6 нием LABEL: MOV AX,BX Здесь LABEL — символическая метка, предшествующая некоторой команде, например 81
Чтобы определить, является этот переход межсегментным или внутрисегментным, ассемблер должен знать, будет ли MEMLOC первым из двух смежных байт (словом - WORD) или четырех смежных байт (двойным словом, или DWORD). Это можно определить в операторе, который распределяет пространство для метки MEMLOC, или явно указать в операторе передачи управления некоторым ключевым словом перед MEMLOC. Например, команда JMP DWORD PTR MEMLOC означает переход с использованием двухсловного указателя с именем MEMLOC и, следовательно, должна быть межсегментным переходом. Внутрисегментный переход определяет новое значение только для регистра IP. Рассмотрим, например, команду перехода со смещением 01А8 в текущем сегменте кода, которая должна заставить программу вернуться на 8 байт к смещению 01А0. Значение смещения 01А0 может находиться в двух байтах команды перехода; так и сделано во многих процессорах. Однако у такого способа есть два недостатка. Во-первых, диапазон многих переходов составляет примерно + 100 байт, а в команде для назначения приходится все равно выделять два байта. Во-вторых, если по какой-то причине вся секция кода от смещения 01 АО до 01В0 должна быть размещена от смещения 0500 до 0510, команда перехода, определяющая смещение 01 АО, больше не будет переходить на 8 байт назад. (Секции кода, которые можно перемещать с последующим правильным выполнением, иногда называются позхщионно-независимым кодом.) Если команда перехода будет определять просто -8, а не 01 АО, то назначение сокращается на один байт и код становится позиционно-независимым. Поэтому в прямых внутрисегментных переходах и вызовах указывается не смещение назначения, а разность (как знаковое число) между смещением назначения и смещением самой команды перехода или вызова. Более того, если эта разность в команде перехода может поместиться в 8 битах (что бывает довольно часто), можно использовать короткую форму команды прямого внутрисегментного перехода, которая на один байт короче обычной команды. Короткая форма команды вызова отсутствует, так как близкие вызовы встречаются не часто. Разумеется, мы не должны сами вычислять эти разности. Нам нужно только записать в команде JMP или CALL символическое имя назначения, а ассемблер выполнит вычитание и образует правильные байты команды. Мы показали две причины применения относительных, а не фактических смещений, как назначений переходов. Покажем, что нет веских причин не пользоваться относительными смещениями. Фактическое смещение представляет собой 16-битное беззнаковое число (из диапазона от 0 до 65535) и может обозначать любую ячейку в текущем сегменте кода. 82
Может ли относительное смещение, будучи знаковым числом (-32768 - + 32767), перекрыть такой же диапазон? Существует ли, например, такое относительное смещение, которое можно использовать в команде перехода со смещением 0 для достижения смещения 65535? Наибольшее положительное отнбсительное смещение равно +32767, а это всего половина расстояния. Поэтому перейдем к отрицательным относительным смещениям. Поскольку команда перехода уже имеет минимальное смещение в сегменте, куда же приведет отрицательное относительное смещение, равное -1? Ответ таков: к наибольшему смещению в сегменте, т.е. 65535 (так спроектирован процессор). Фактически команда перехода со смещением 0 может достичь любого смещения от 32768 до 65535 при использовании отрицательного относительного смещения. Теперь должно быть ясно, что относительные смещения могут привести от команды перехода, расположенной в любом месте сегмента, к любой ячейке в сегменте. Наше рассуждение об использовании относительных смещений вместо фактических неприменимо ни к косвенным переходам и вызовам, ни к межсегментным. Объясняется это несколькими причинами: 1. Косвенные переходы и вызовы не определяют назначение, а показывают, где его найти. Несколько команд косвенных переходов и вызовов могут определять одно и то же место, в котором находится назначение. Поэтому принцип относительного смещения теряет смысл и неизвестно, относительно какой команды его находить. 2. Межсегментные переходы и вызовы задают назначения в другом сегменте кода. Если секция кода, содержащая межсегментные переходы или вызовы, перемещается в памяти, назначение, будучи в другом сегменте, совсем не обязательно будет изменяться. Следовательно, применение относительных смещений не приводит к позиционно-независимому коду. 3. Назначение межсегментного перехода или вызова не обязательно находится рядом, поэтому нельзя ожидать экономии байт при использовании относительных смещений. В заключение несколько слов о команде возврата. Имеется разновидность команды возврата, которая после восстановления значений содержимого регистра IP и, возможно, регистра CS (их значения извлекаются из стека) прибавляет к значению указателя стека константу, содержащуюся в команде как непосредственный операнд. В результате из стека извлекаются и уничтожаются дополнительные элементы. Значения этих элементов можно поместить в стек до команды вызова, т.е. передать вызываемой процедуре параметры. Когда процедура завершается и осуществляет возврат, параметры больше не нужны. Приведенная выше команда возврата оказывается удобным средством удаления параметров процедуры. Если бы такой команды не было, параметры пришлось бы удалять из стека следующим образом: 83
1. До возврата процедура считывает из стека сохраненное значение в регистре IP (и, возможно, в регистре CS) и помещает его куда-то в память. Этим действием "раскрываются" параметры, находящиеся в стеке под сохраненными значениями в регистрах IP и CS. 2. Затем процедура прибавляет к содержимому регистра SP константу, что эквивалентно удалению параметров. 3. После этого процедура возвращает в стек "отложенные" значения в регистрах IP и CS. 4. Выполняется команда возврата. Как видно, команда "возврата и удаления" значительно упрощает* удаление параметров из стека. Можно также удалить параметры путем инкремента содержимого указателя стека после того, как процедура выполнит команду возврата. На первый взгляд это представляется почти столь же эффективным, как и команда "возврата и удаления". Но производить инкремент содержимого указателя стека сама процедура не может (уже произведен возврат из нее), поэтому его нужно выполнять в каждом том месте, куда возвращается процедура. Как только вы убедитесь, что процедура может вызываться из многих мест, такое решение будет менее привлекательным. В команде "возврата и удаления" число параметров (т.е. значение, которое прибавляется к содержимому регистра SP) представлено 16 битами. Конечно, в подавляющем большинстве случаев было бы достаточно 8 бит и команда оказалась бы на один байт короче. Однако в тех редких случаях, когда 8 бит недостаточно, пришлось бы удалять параметры тем менее удобным способом, который рассмотрен выше. Поэтому в команду был введен лишний байт. 3.7. КОМАНДЫ УСЛОВНОЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ В процессоре 80286 имеются команды условных переходов, которые совместно с командами сравнения СМР находят отношение между двумя числами. Определение отношения осуществляется в два этапа. Сначала процессор выполняет команду сравнения, которая производит вычитание двух чисел, устанавливает по результату флажки и уничтожает разность. Затем он выполняет команду условного перехода, которая проверяет флажки и производит переход, если флажки показывают, что числа удовлетворяют заданному отношению. Предположим, например, что необходимо выполнить конкретные команды, если число в регистре ВН равно числу в регистре BL: 1. Сравнить содержимое регистров ВН с BL (устанавливаются флажки). 2. Перейти к шагу 5, если ZF = 0. 3. Специальные команды... 84
4.... выполняемые, если содержимое регистра ВН равно содержимому регистра BL. В этом примере по команде сравнения производится вычитание значения содержимого регистра BL из значения в регистре ВН и по результату устанавливаются флажки. Если ВН = BL, то результат равен нулю и флажок ZF устанавливается в 1. Следовательно, проверка на равенство заключается в проверке состояния флажка ZF и именно это делает команда условного перехода на шаге 2: если ВН Ф BL, ZF = 0 и шаги 3-4 пропускаются. Каждая из команд условного перехода состоит из 8-битного кода операции и 8 бит, определяющих назначение перехода. Оно равно разности между смещением назначения и смещением команды условного перехода. Как мы уже говорили, это ведет к позиционно-незавиеимому коду (переходы относительны) и к компактному коду (назначение определяется всего 8 битами). Но при этом назначение перехода будет в сравнительно небольшом диапазоне (примерно 127 байт) от команды условного перехода. Для двух форм команд условных переходов ("близкая" и "далекая") потребовалось бы слишком много кодов операций. Случай "близкого" перехода встречается гораздо чаще, поэтому было решено выбрать этот вариант ("далекий" условный переход всегда можно осуществить двумя командами, в которых "близкий" условный переход обходит "далекий" безусловный переход). Кроме проверки на равенство часто нужно знать, какое число больше, и при этом возникает интересный вопрос: является ли число 1111 1111 больше числа 0000 0000? Ответ может быть и положительным и отрицательным. Если числа считать беззнаковыми, то первое число равно 255 и оно, конечно, больше 0. Но если числа считать знаковыми, то первое число равно -1, т.е. меньше 0. Следовательно, отношение "больше" и "меньше" зависит от того, считаются числа беззнаковыми или знаковыми, и целесообразно ввести новые термины, позволяющие различать эти два случая. При сравнении чисел как знаковых мы пользуемся терминами меньше и больше, а при сравнении беззнаковых чисел - терминами ниже и выше. Поэтому число 1111 1111 выше числа 0000 0000 и одновременно меньше его. А число 0000 0000 ниже и меньше числа 0000 0001. Следовательно, между двумя числами существуют отношения равны, выше, ниже, меньше и больше. Каждое из этих условий можно определить по состояниям флажков после команды сравнения (см. табл. 3.9). В процессоре 80286 имеются команды условных переходов, которые проверяют по состояниям флажков, удовлетворяется конкретное отношение или нет. Имеются следующие условные переходы: 85
Название Смысл JE Перейти, если равны JNE Перейти, если не равны JL Перейти, если меньше JNL Перейти, если не меньше JG Перейти, если больше JNG Перейти, если не больше JB Перейти, если ниже JNB Перейти, если не ниже JA Перейти, если выше JNA Перейти, если не выше Можно придумать и другие отношения, например "меньше или равны", но это то же самое, что и "не больше". Вот список альтернативных названий приведенных выше команд: Название Альтернативное название Смысл альтернативного названия JE JZ Перейти, если нуль JNE JNZ Перейти, если не нуль JL JNGE Перейти, если не больше или равны JNL JGE Перейти, если больше или равны JG JNLE Перейти, если не меньше или равны JNG JLE Перейти, если меньше или равны JB JNAE Перейти, если не выше или равны JNB JAE Перейти, если выше или равны JA JNBE Перейти, если не ниже или равны JNA JBE Перейти, если ниже или равны Приведем также фактические состояния флажков для различных команд условных переходов. 86
Название команды JE/JZ Состояние флажков ZF-1 ZF-0 (SF • OF) - 1 (SF • OF)« 0 (SF* OF)V ZF-0 (SF * OF) V ZF = 1 CF= 1 CF = 0 CF V ZF » 0 CF V ZF » 1 JNE/JNZ JL/JNGE JNL/JGE JG/JNLE JNG/JNE JB/JNAE JNB/JAE JA/JNBE JNA/JBE Имеются команды условных переходов, которые связаны не с отношениями между числами, а с состоянием конкретного флажка. Команды JZ и JNZ, приведенные выше, фактически проверяют состояние флажка нуля, а команды JB и JNB - флажка переноса. Приведем и другие команды условных переходов, которые проверяют состояние конкретного флажка: Название Смысл Состояние флажка JS Перейти, если знак SF-1 JtfS Перейти, если не знак SF-0 JO Перейти, если переполнение OF-1 JNO Перейти, если не переполнение OF = 0 JP Перейти, если паритет PF»1 JNP Перейти, если не паритет PF-0 Для последних двух команд имеются альтернативные названия: Название Альтернативное название Смысл альтернативного иазратш JP JPE Перейти, если паритет четный JNP JPO Перейти, если паритет нечетный В большинстве современных процессоров имеются средства прерывания их внешними устройствами. Они освобождают процессор от периодической проверки необходимости обслуживания устройств. Например, вместо того, чтобы заставить процессор часто опрашивать клавиатуру, не нажата ли клавиша, и большую часть времени получать отрицательные ответы, гораздо эффективнее разрешить процессору игнорировать клавиатуру, но позволить клавиатуре привлекать внимание процессора при нажатии клавиши. Первый способ называется опросом (полингом), а второй - прерыванием. 3.8. ПРЕРЫВАНИЯ 87
Механизм прерываний. Процессор 80286 имеет два входа, по которым внешние устройства могут привлечь его внимание: вход NMI немаскируемого прерывания и вход INTR маскируемого прерывания. Рассмотрим вначале вход NMI. Когда внешнее устройство формирует сигнал на входе NMI, процессор прекращает свои действия (но не в середине команды) и реагирует на прерывание. Однако процессор может выполнять очень важную задачу, поэтому внешние устройства не должны прерывать процессор по входу NMI за исключением появления катастрофических событий. Примером таких событий служит сообщение о том, что напряжение сети упало до 100 В и продолжает уменьшаться, т.е. возник "отказ сети". Ясно, что внешнее устройство должно известить процессор о том, что ему осталось "недолго жить". За несколько оставшихся миллисекунд процессор может привести "свои дела в порядок" (например, передать важные результаты в безопасное место), прежде чем генератор синхронизации остановится. Если таких катастрофических событий нет, внешнее устройство имеет возможность прервать процессор по входу INTR. Процессор может проигнорировать этот входной сигнал. Реакция зависит от сосгоя- нгя флажка разрешения прерываний IF; когда IF = 0, процессор не реагирует на входные сигналы INTR. Говорят, что прерывания разрешены, если IF = 1, и запрещены, если IF = 0. Команды, воздействующие на флажок W, мы рассмотрим далее. Кроме сигнала на входе INTR, внешнее устройство должно сообщить процессору причину прерывания. Причин прерываний по входу INTR может быть довольно много (например, до 256), а причина прерывания по входу NMI всего одна (надвигающаяся катастрофа). Поэтому по запросу процессора внешнее устройство сообщает число из диапазона 0 255, соответствующее причине прерывания по входу INTR и называемое типом прерывания. Для каждого типа прерывания у процессора есть программа, которую он должен выполнить до возобновления прерванной задачи. Адреса этих программ находятся в 256-элементной таблице. Каждый ее элемент состоит из четырех байт и содержит значения регистров IP и CS, соответствующие началу программ для конкретных типов прерываний. Таблица начинается с адреса памяти 0, как показано на рис. 3.17. Программы, выполняющиеся при возникновении прерываний, часто называются процедурами прерываний. Рассмотрим действия процессора, когда он воспринимает сигнал INTR и прерывания разрешены (IF = 1). После завершения текущей команды процессор прекращает свое операции и готовится выполнить процедуру прерывания конкретного типа. Прежде всего он сохраняет всю важную информацию о своих действиях, чтобы по завершении процедуры прерывания их можно было возобновить. Удобно сохранить эту информацию в стеке. Сохраняет же процессор текущие состояния всех флажков и теку- 88
Адрес памяти Память 00000 00001 00002 00003 00004 00005 00006 00007 003FC 003FD 003FE 003FF Рис. 3.17. Таблица адресов прерываний щее содержимое регистров IP и CS. Затем процессор получает от внешнего устройства тип прерывания и помещает в IP и CS элементы таблицы, соответствующие этому типу прерывания. Предположим, например, что внешнее устройство сообщает о типе прерывания 01. В этом случае в регистр IP помещается 16-битное значение по адресу 00004, а в CS - 16-битное значение по адресу 00006. Поэтому следующей будет выполняться первая команда процедуры прерывания, соответствующей типу прерывания 1. Когда процессор воспринимает прерывание по входу NMI (независимо от состояния флажка IF), он реагирует на него так же, как на прерывание по входу INTR, за одним исключением. Ему не нужно получать от внешнего устройства тип прерывания, так как причина прерывания по входу NMI всего одна. Для локализации процедуры прерывания NMI отведен элемент с типом 2 в таблице прерываний; поэтому в регистры IP и CS помещаются элементы таблицы для типа 2. Итак, мы рассмотрели прерывания, формируемые внешними устройствами, и их естественно называть внешними прерываниями. Но процессор сам генерирует внутренние прерывания, если при выполнении некоторых команд происходит что-то неожиданное. Такие неожиданные события называются особыми случаями, и они обычно свидетельствуют о серьезной ошибке. Например, команды деления вызывают прерывание типа 0, если делитель равен нулю. Следовательно, в таблице элемент для типа 0 (еще } IP > Прерывание типа 0 IP CS Прерывание типа 1 89
4 один зарезервированный тип прерывания) должен содержать значения для регистров IP и CS процедуры прерывания, которая обрабатывает такую ситуацию. Первый 31 элемент в таблице прерываний зарезервирован для внутренних прерываний и прерывания NMI. Они показаны на рис. 3.18. Микропроцессор 8086 генерирует только первые пять внутренних прерываний. Ни одно из зарезервированных прерываний не зависит от состояния флажка! разрешения прерываний IF. 00 ■ IP « 01 02 • CS ► 03 04 05 ' IP 06 ' CS 07 08 • IP 09 OA • CS > OB ОС • IP 00 0Е • CS OF 10 11 IP 12 • CS ► 13 14 15 • IP 16 ' CS 17 16 ' IP 19 1А ■ CS IB 1С • IP 1D 1Е • CS 1F 20 21 * IP 22 ' CS 23 24 25 ' IP 26 ' CS 27 « Тип 0 зарезервирован для деления на 0 Тип 1 зарезервирован для пошаговой работы Тип 2 зарезервирован для прерываний NMI Тип 3 зарезервирован для однобайтной команды INT Тип 4 зарезервирован для знакового переполнения (INTO) Тип 5 зарезервирован для выхода за границу BOUND Зарезервирован для недействительного кода операции Тип 7 зарезервирован для отсутствующего сопроцессора Тип 8 зарезервирован для слишком малого предела таблицы прерываний Тип 9 зарезервирован для превышения сегмента сопроцессором Рис. 3.18. Зарезервированные типы прерываний Типы 10-12 зарезервированы^ для будущих расширений Тип 13 зарезервирован для особого случая превышения сегмента Типы 14 и 15 зарезервированы для будущих расширений Тип 16 зарезервирован для ошибки сопроцессора Типы 17-31 зарезервированы для будущих расширений Еще одним примером неожиданного результата является знаковое переполнение. Однако при этом процессор не генерирует внутреннего прерывания. Объясняется это тем, что в знаковой и беззнаковой арифметике применяется одна и та же команда ADD и процессор не знает, выполнялось ли фактически знаковое сложение (это же относится и к вычитанию). Но в процессоре предусмотрена однобайтная команда INTO, которая генерирует прерывание типа 4, если флажок переполнения OF = 1. Ее нужно вводить после каждой арифметической команды со знаковыми числами, если существует потенциальная возможность возникновения переполнения. 90
Внутреннее прерывание типа 6 генерируется, если встречается недействительная операция. Другие внутренние прерывания, показанные на рис. 3.18, мы рассмотрим в дальнейшем. Обратимся теперь к самой процедуре прерывания. Она может не заботиться о состояниях флажков, так как они запомнены в стеке. Но если процедура прерывания изменяет любые другие важные данные (которые будут использоваться прерванной программой, например, содержимое регистра АХ), она должна вначале сохранить их. Перед завершением процедура прерывания должна восстановить запомненные ею важные данные. Наконец, процедура прерывания заканчивается выполнением команды IRET (возврат из прерывания), которая восстанавливает из стека запомненное содержимое регистров IP, CS и флажков. Отметим, что возврат из прерывания отличается от межсегментного возврата только восстановлением состояний флажков, но обе команды возвращают содержимое регистров IP и CS. Обычно с прерываниями ассоциируется и команда останова HLT. Она прекращает действия процессора, причем содержимое регистров CS и IP показывает на команду после HLT. Когда возникает прерывание, процессор сохраняет в стеке эти значения из регистров CS и IP и начинает выполнять команды процедуры прерывания. Когда встречается команда IRET, запомненные значения регистров CS и IP восстанавливаются. Теперь процессор не помнит, что ранее он ожидал прерывания. Поэтому он продолжает выполнять команду, на которую показывают регистры CS и IP, т.е. команду, следующую за командой HLT. По существу, команда HLT превращается в средство запуска процессора процедурой прерывания. Таким образом, до 256 процедур прерываний находятся в разных областях памяти и ожидают появления прерываний, которые приводят и> в действие. Некоторые из них удобно вызывать и тогда, когда прерываний нет. Поскольку значения в регистрах CS и IP, необходимые для выполнения процедур, находятся в четырех смежных байтах памяти, можно вызвать процедуру прерывания, выполнив команду косвенного межсегментного вызова, которая определяет эти четыре байта. Но здесь кроется опасность! Ведь процедура прерывания не заканчивается обычной командой возврата; она заканчивается командой IRET, которая извлекает из стека флажки. Поэтому для правильной работы флажки должны быть в стеке. Для этого перед командой косвенного межсегментного вызова можно поставить команду включения в стек флажков PUSHF, но такой прием становится громоздким. Было бы хорошо иметь одну команду, которая выполняет все, что делает процессор при распознавании прерывания, но с одним исключением - тип прерывания содержится в команде, а не выдается внешним устройством. Такой командой и является команда прерывания 91
INT где п - целое число между 0 и 255. Состояние флажка разрешения прерываний IF не влияет на выполнение команды INT. Команды прерываний процессора 80286 приведены в табл. 3.20. Таблица 3.20. Команды прерываний Мнемоника команды Описание команды INTn Прерывание типа п (два байта) INT Прерывание типа 3 (один байт) INTO Прерывание при переполнении IRET Возврат из прерывания HLT Останов Требования отладчика. Отметим, что имеются две формы команды INT. В первой форме длина команды составляет два байта, причем второй байт определяет тип прерывания. Во второй форме в команде длиной один байт тип прерывания определен неявно (см. зарезервированные типы на рис. 3.18). Тип 3 не столь важен (им мог бы быть любой тип), как важна длина команды в один байт. Такая команда INT играет существенную роль в работе программы-отладчика. Для понимания этой роли рассмотрим работу отладчика несколько подробнее. Отладчик - это интерактивная программа, помогающая выяснить, почему разработанная нами программа не работает правильно. Обычно мы хотим сообщить отладчику о выполнении нашей плохой программы до поступления команды по какому-то конкретному адресу, например 100. Это действие называется "установкой контрольного останова" по адресу 100. Отладчик помещает по адресу 100 команду, которая передает ему управление. Теперь он может запустить программу, а когда программа достигнет адреса 100, управление будет передано отладчику. Конечно, отладчик должен сохранить исходное содержимое по адресу 100 до установки контрольного останова и восстановить его после возврата управления. Теперь возникает вопрос о том, какую команду процессора 80286 встроить по адресу 100. При наличии в любой момент времени всего одного останова можно было бы обойтись командой перехода. Однако при нескольких остановах отладчик должен знать, какой останов достигнут. Идеальной оказывается команда INT, потому что она сохраняет информацию (содержимое регистров CS и IP), локализующую останов. Применение двухбайтной команды INT означает необходимость сохранения содержи- 92
мого памяти по адресам 100 и 101. Отладчик должен сохранить, а затем восстановить это содержимое. В большинстве случаев при этом особых трудностей не возникает. Однако рано или поздно мы напишем программу, в которой команда по адресу 101 выполняется прежде команды по адресу 100 (см. пример на рис. 3.19). Но теперь на месте команды по адресу 101 находится второй байт команды INT, встроенной по адресу 100. Вот почему в отладчике должна применяться однобайтная команда INT. Отладчик использует команду INT для генерирования прерываний типа 3 при отладке программ; следовательно, в наших программах, если мы хотим работать с отладчиком, нельзя применять однобайтную команду INT или прерывания типа 3. Считать значение в АХ Выполнить обработку с привле чением значения из АХ Адрес Команда . 90, 91 Считать значение из порта 2 в АХ (IN 1 92, 93 Перейти к 101 (JMP - 2 байта) 94,..., 99 Обработка с привлечением значения из АХ 100 Инкремент АХ (INC — 1 байт) 101, 102 Сравнить АХ с 5 (СМР - 2 байта) 103, 104 Перейти к 94, если меньше (JL - 2 байта) 2 байта) Рис. 3.19. Программа, которая выполняет команду по адресу 101 раньше, чем команду по адресу 100 93
С отладчиками используется еще одно средство - флажок трассировки TF. Когда этот флажок установлен в состояние 1, процессор выполняет одну команду, а затем генерирует прерывание типа 1 (рис. 3.20). Другими словами, отладчик выполняет программу по отдельным командам и анализирует, что происходит после каждой команды. Такой режий работы называется пошаговой (покомандной) работой. (Не беспокойтесь б1 повторяющихся цепочечных командах; в этом режиме прерывание возникает после каждого повторения, а не по окончании всей команды.) Программа Команда Команда Команда Команда Команда Команда Команда Команда Команда Команда Команда Команда Команда **Показывает появление прерывания типа 50 Рис. 3.20. Выполнение программы в пошаговом режиме Отладчик может заставить программу работать в пошаговом режиме, модифицировав флажки, запомненные в стеке предыдущим прерыванием (так что сохраненное значение TF будет 1), а затем выполнив команду IRET. Так как переход в пошаговый режим осуществляет отладчик, а не программа, команды установки и сброса флажка TF не нужны. Предположим, например, что программа выполняется обычным образом. Мы хотим остановить ее и перевести в пошаговый режим. Требуется после каждой команды npocMofpeib содержимое всех регистров, чтобы убедиться, что программа работает правильно. Мы можем остановить программу, подав как-то сигнал на вход INTR и сообщив процессору тип прерывания, например 50. Процессор прекратит выполнение программы (если IF = 1) и сохранит в стеке значения флажков и регистров CS и IP. Затем он начнет выполнять процедуру для прерывания типа 50. В этой процедуре мы предусмотрели обращение к стеку и установку сохраненного значения состояния флажка TF в 1. Затем процедура прерывания выполняет команду IRET и восстанавливает значения регистров IP, CS и флажков. В них окажутся те 94 Процедура прерывания типа 50 Модифицировать образ TF в стеке Процедура прерывания типа 1 Индицировать вес регистры IRET
же значения, которые были до прерывания программы, но теперь TF = 1. В результате программа выполняет одну команду, а затем запоминает в стеке значения флажков (с TF = 1) и регистров CS и IP и начинает выполнять процедуру для прерывания типа 1. Чтобы предотвратить пошаговую работу в процедуре прерывания, флажок TF автоматически сбрасывается после запоминания состояний флажков в стеке. Процедура для прерывания типа 1 должна индицировать содержимое всех регистров. Последней командой в ней вновь будет команда IRET, которая устанавливает значения содержимого регистров IP, CS и флажков. Так как флажок TF снова оказывается в состоянии 1, описанный процесс повторяется (см. рис. 3.20). Процедуры для типов прерываний 1 и 50 являются частью отладчика. Изменение стеков. Рассмотрим команду, которая передает новое значение в сегментный регистр стека SS. Она является одной из двух команд MOV, которые необходимо выполнить, чтобы изменить стек (это полезная операция, когда процессор попеременно выполняет несколько программ, у каждой из которых есть свой стек). Вторая команда MOV передает новое значение в указатель стека SP. После выполнения обеих команд MOV регистры SS и SP совместно определяют положение вершины нового стека. Однако после выполнения первой команды MOV, но до начала выполнения второй, комбинация регистров SS и SP не имеет никакого смысла; ясно, что она не определяет вершины никакой области, отведенной для стека (мы исключаем случайное совпадение). Сложность возникает, если во время смены стека кто-то попытается осуществить включение в стек. А ведь именно такая попытка возникнет при внешнем прерывании или при прерывании пошаговой работы. Чтобы предотвратить такую ситуацию, процессор 80286 не воспринимает никаких прерываний после выполнения команды, которая загружает новое значение в регистр SS. 3.9. ФЛАЖКОВЫЕ КОМАНДЫ Процессор 80286 имеет команды для установки и сброса флажка переноса (STC, CLC), флажка направления (STD, CLD) и флажка прерывания (STI, CLI). Имеется также команда инвертирования флажка переноса (CMC). Эти команды приведены в табл. 3.21. Мы уже рассматривали использование флажков процессора: флажок переноса предназначен для арифметики многократной точности, флажок направления - для обработки цепочек и флажок прерывания - для разрешения и запрещения прерываний. 95
Таблица 3.21. Операции над флажками Мнемоника команды Описание команды CLC сбросить флажок переноса CMC инвертировать флажок переноса STC установить флажок переноса CLD сбросить флажок направления STD установить флажок направления CLI сбросить флажок разрешения прерываний STI установить флажок разрешения прерываний 0 -CF 1 - CF - CF О-IF 0-DF 1 - CF 1 - DF 1 - IF ЗЛО. КОМАНДЫ СИНХРОНИЗАЦИИ Одним из средств синхронизации процессора с внешними устройствами являются прерывания, но в его архитектуре реализованы еще две формы синхронизации. Первая относится к использованию подчиненного процессора, выполняющего то, что не может делать процессор 80286, а вторая - к разделению ресурсов, например памяти, с другими процессорами в мультипроцессорной системе. Остановимся на этих формах подробнее. Подчиненные процессоры. Хотя система команд процессора 80286 довольно мощная, в ней, конечно, нет множества команд, рперирующих, например, числами с плавающей точкой. Разумеется, можно написаяь процедуру для сложения двух чисел с плавающей точкой, но намного эффективнее иметь команду сложения чисел с плавающей точкой. Еще лучше иметь отдельный процессор, способный выполнять операции над числами с плавающей точкой, и подчинить его процессору 80286. Имея такой процессор, можно включать в программу команды операций над числами с плавающей точкой; встретив такую команду, процессор 80286 будет вызывать для ее выполнения процессор с плавающей точкой. Такой процессор 80287 рассматривается в следующей главе. Подчиненный процессор работает только во взаимодействии с процесс сором 80286. В частности, когда процессор 80286 встречает специальную команду ESC (эта мнемоника обозначает все команды, в которых процессору 80286 требуется помощь), он извещает подчиненный процессор. Команда ESC показывает, какую операцию должен выполнить подчиненный процессор (эту информацию процессор 80286 игнорирует). Но команда ESC задает для подчиненного процессора операнд в памяти. Эту информацию процессор 80286 использует: он вычисляет адрес памяти операнда, а затем считывает или записывает в память по запросу подчиненного процессора. (На 96
самом деле взаимодействие между процессорами осуществляется несколь* ко сложнее.) Команда ESC применяется совместно с командой ожидания WAIT, обеспечивающей синхронизацию процессоров; с ее помощью процессор 80286 проверяет, когда подчиненный процессор заканчивает свою операцию. Когда подчиненный процессор работает, он выдает сигнал высокого уровня на своем выходе BUSY (занят), а команда WAIT приостанавливает процессор 80286, если он обнаруживает такой сигнал на выходе BUSY. Как и в цепочечных командах, команду WAIT разрешается прерывать до ее завершения. Подробнее о командах ESC и WAIT мы поговорим в гл. 4. Разделение ресурсов.: Еще одна форма синхронизации двух процессоров связана с разделением общего ресурса. Рассмотрим, например, систему резервирования авиабилетов, в которой несколько процессоров обращаются к общей базе данных, находящейся в разделенной памяти. Как мы покажем, база данных может быть испорчена, если двум процессорам разрешить модифицировать ее одновременно. Предположим, что один из процессоров хочет изменить платеж Гарри Джонса за рейс 351 с кредитной карточки компании VISA на кредитную карточку компании American Express. Прежде всего процессор будет просматривать список пассажиров рейса 351 до тех пор, пока не обнаружит данные Гарри Джонса. После этого он запишет новую информацию в поле кредитной карточки этой записи. Предположим, что когда производится эта модификация, второй процессор занят проверкой кредитных карточек всех пассажиров. Когда этот процессор начинает заниматься Гарри Джонсом, он может обнаружить часть старого номера и часть нового, потому что первый процессор еще не закончил корректировку. В результате компания, обслуживающая по кредитным карточкам, отвергнет такой неправильный номер. Наверное, можно было бы решить проблему, если бы первый процессор изменял весь номер одной командой. Для этого лучше всего воспользоваться командой повторяющейся пересылки цепочки REP MOVS. Теперь вроде бы второй процессор не сможет считать неправильный номер, но только если он не попытается сделать это в середине команды REP MOVS первого процессора. Будьте уверены, что однажды это произойдет! Таким образом, изменение номера карточки в одной команде решает проблему не полностью, а просто уменьшает вероятность ее возникновения. Что действительно нужно сделать, чтобы процессор "Гарри" запретил всем остальным процессорам обращаться к разделенной памяти на время выполнения им команды REP MOVS? В процессоре 80286 это обеспечивается предшествующим команде префиксом блокировки LOCK: LOCK REP MOVS ; Команда с двумя префиксами 4 Заказ 6126 97
Такая команда заставит процессор выдать сигнал LOCK на все время выполнения команды. Теперь систему резервирования авиабилетов можно спроектировать так, чтобы дать исключительное право на доступ к памяти любому процессору, выдавшему сигнал блокировки. Если никакой другой процессор не может обратиться к разделенной памяти, то он не сможет считать запись во время ее корректировки. Рассмотрим еще один пример, в котором требуется выдавать сигнал блокировки. Пусть некоторый процессор хочет зарезервировать билет Уильяму Смиту на рейс 351. Она находит первую незанятую запись в списке пассажиров и с помощью команды пересылки цепочки помещает в нее информацию об Уильяме Смите. Предположим, что другой процессор хочет зарезервировать билет Роберту Грину. Он может найти ту же свободную запись (перед тем, как процессор "Смита" что-то туда запишет) и попытается поместить туда информацию о Роберте Грине. Неважно, какой процессор первым будет выполнять команду пересылки цепочки; главное заключается в том, что один из них "перезапишет" информацию другого. Здесь префикс LOCK перед командой пересылки цепочки не помогает. К счастью, и такую ситуацию можно предотвратить, если восгользо- ваться семафором. Семафор - это просто ячейка памяти, которая сообщает процессору о том, что другой процессор работает с разделенной памятью, и заставляет первый процессор ожидать освобождения памяти. Рассмотрим, как работает семафор. Любой процессор, желающий обратиться к разделенной памяти, вначале должен проверить семафор. Если семафор имеет значение 1, процессор вынужден ожидать освобождения памяти. Если же семафор имеет значение 0, процессор должен установить его значение в 1 (запрещая другим процессорам обращаться к памяти), модифицировать содержимое памяти нужным образом, а затем сделать значением семафора 0. Конечно, теперь возникает проблема в том, что оба процессора одновременно проверяют семафор и обнаруживают его нулевое значение. Оба процессора установят его значение в 1 и оба попытаются одновременно работать с разделенной памятью. Такую ситуацию можно предотвратить, если считывать значение семафора и устанавливать его в 1 одной командой и выдавать на время ее выполнения сигнал блокировки. Такой командой оказывается команда обмена XCHG, используемая следующим образом: SPIN: СМР MOV XCHG ВХ, 1 ВX,SEMA BX.0 ; Значение для семафора ; SEMA —> ВХ и 1 —> SEMA ; Семафор был равен 0 ? JNE SPIN ? Повторять опрос, если нет BOUND ВХ,MEMLOCS 98
С командой XCHG надо было бы указать префикс LOCK, чтобы на время ее выполнения действовал сигнал блокировки. Однако применение команды XCHG для установки и проверки семафоров настолько важно, что процессор 80286 автоматически выдает сигнал блокировки независимо от наличия или отсутствия префикса LOCK. 3.11. поддержка языков высокого уровня Процессор 80286 имеет три команды (BOUND, ENTER и LEAVE), которые упрощают программирование на языках высокого уровня. Например, команда BOUND осуществляет контроль массивов, а команды ENTER и LEAVE реализуют служебные функции, связанные с процедурами. Программист, работающий на языке высокого уровня, совершенно не касается этих функций, так как все необходимые команды генерирует компилятор. В микропроцессоре 8086 указанных команд нет. Команда BOUND проверяет, находится ли знаковое значение из 16-битного регистра в заданных пределах. Пределы находятся в двух смежных словах памяти. Значение в 16-битном регистре должно быть больше или равно значению в первом слове и меньше или равно значению во втором слове; в противном случае генерируется прерывание типа 5. Пример команды BOUND имеет вид: BOUND В X,MEMLOCS Эта команда удобна для проверки нахождения индекса массива в допустимых пределах; если этого нет, очевидно, в программе имеется ошибка. Для понимания служебных функций, относящихся к процедурам, необходимо разобраться в обращениях к переменным на языке высокого уровня с блоковой структурой. Рассмотрим следующий фрагмент программы на языке Паскаль (мы не предполагаем знание читателем языка Паскаль): procedure А; {имя процедуры} var al, а2 : integer; (объявления переменных для процедуры А> begin al s* а2 + 1 ; end Стело процедуры А> Процедура состоит из раздела объявлений и тела. Тело процедуры образуют те действия (исполняемые операторы), которые должна выполнять процедура. Операторы в теле процедуры могут обращаться к любым переменным, описанным в разделе объявлений. Кроме переменных, в разделе объявлений можно указать и другие процедуры, например 99
procedure A; var al, a2 : integer; (имя процедуры} {объявления переменных для процедуры А> procedure В; var Ы, Ь2 : integer; begin Ь2 := al + 2; end; (объявление процедуры для процедуры А> procedure С; var cl, с2 : integer; begin cl := a2 ♦ 3; end; (объявление процедуры для процедуры А> begin al := а2 + 1; end (тело процедуры А> Отметим, что к переменным и процедурам, находящимся в разделе объявлений процедуры А, могут обращаться операторы из процедур В и С, а также процедуры А. Однако к элементам, указанным в разделе объявлений процедуры Смогут обращаться только операторы из тела процедуры В; аналогичное ограничение касается и процедуры С. Компилятору было бы просто генерировать код, который размещает все переменные в памяти при загрузке программы и никогда не изменяет местоположения ни одной переменной. Тогда к каждой переменной можно было бы обратиться в режиме прямой адресации. Однако в языке Паскаль процедура может вызывать саму себя. Такая возможность удобна (хотя и не столь важна) для вычисления, например, факториала (напомним, что х! = х *(х - 1)!). Каждый вызов такой процедуры должен иметь свою уникальную память для переменных, объявленных в процедуре. Следовательно, переменным нужно распределять память при каждом вызове процедуры и освобождать ее при каждом возврате. Удобным хранилищем для подобных переменных оказывается стек. Для рекурсивных процедур компиляторы языка Паскаль обычно генерируют код, который при вызове процедуры выполняет следующие действия: распределяет пространство в стеке (называемое стековым кадром) для переменных, объявленных в процедуре, и устанавливает регистр BP на начало (т.е. высший стековый адрес) стекового кадра. Так как фактическая ячейка памяти переменной будет другой при каждом вызове процедуры (стековый кадр не обязательно всякий раз начинается в одном и том же месте памяти), прямую адресацию памяти использовать нельзя. Константой остается только расстояние переменной от начала стекового кадра. Следовательно, генерируемый компилятором код для обращений к таким переменным должен быть представлен в терминах этого расстояния. В результате получается адресация, называемая адресацией относительно стекового кадра. Если, например, переменная а2 находится на расстоянии 8 байт от начала стекового кадра, а переменная 100
al - на расстоянии 6 байт, компилятор в теле процедуры Л может образовать следующие команды: MOV А1_,-8СВРЭ ; Передать в AL значение а2 INC AL ; Прибавить 1 к значению а2 MOV -6CBP3,AL ; Передать сумму в al Теперь возникает проблема использования адресации относительно стекового кадра для доступа к переменным, объявленным в одной процедуре, из тела другой процедуры. Например, переменная а2 используется в процедуре В, но объявлена в процедуре А. Когда выполняется процедура В, последним распределенным стековым кадром является стековый кадр для процедуры В, а он не содержит переменной а2. Картина стекового кадра в этот момент времени показана на рис. 3.21,а. Каждая переменная, к которой обращается данная процедура, находится либо в стековом кадре данной процедуры, либо в стековом кадре той процедуры, в которую вложена данная. Следовательно, стековый кадр для данной процедуры должен содержать указатели на каждый из ранее созданных стековых кадров. Получающийся список указателей называется индикатором. Стековый кадр с индикатором показан на рис. 3.21,6. Теперь компилятор может образовать код, который использует индикатор для локализации соответствующего стекового кадра, а затем прибавляет расстояние от начала стекового кадра для локализации переменной. t Большие адреса Память для а1,а2 Рост стека } Стековый кадр для А I I Динами- J I. ческая Л у связь I | I) Индикатор Динамическая связь __Л Индикатор Стековый Память] С кадр для А ЧПеремен-! j ные 1 1 Индикатор Память") L Стековый чПеремен-Л кадр J"bie I для А для а1,а2. W"*tnanMBl 1 Стековый кадр Память *"".?'■ Г для В Для el J H4J L Г i Перемет - ные I Стековый кадр для В ДЛЯ ,а1. а2. Память для с1 с2 Память для в1 И2- "1 атор. ен^ \тор к Динами-i V ческая |СВЯЗЬ Индикатор». Стековый кадр для С „Перемен ные Динами-^ 1 ческая ~ связь ►Индикатору Стековый кадр для В „Перемен- а) б) в) Рис. 3.21. Пример стековых кадров: а — стековый кадр, показывающий только распределенные переменные, б — стековый кадр, показывающий индикаторы и распределенные переменные; в — стековый кадр, показывающий динамические связи, индикаторы и распределенные переменные 101
Когда происходит выход из данной процедуры, необходимо выполнить следующие действия: освободить стековый кадр и скорректировать регистр BP так, чтобы он указывал на стековый кадр, бывший текущим стековым кадром во время вызова процедуры (динамическая связь). Отметим, что им не обязательно будет стековый кадр той процедуры, в которую вложена данная процедура (статическая связь). Например, процедура В вложена в процедуру А, но может вызываться из процедуры А или процедуры С. Освобождение стекового кадра реализуется передачей текущего значения регистра BP в регистр SP (т.е. превращением регистра SP в указатель начала текущего стекового кадра). Коррекция содержимого регистра BP невозможна, если не сохранить значение регистра BP (динамической связи) до вызова данной процедуры. Динамическую связь можно поместить в стековый кадр перед индикатором. Стековый кадр, содержащий динамическую связь для случая, когда процедура В вызвана из процедуры С, показан на рис 3.21,в. Таким образом, в начале каждой процедуры компилятор должен генерировать код для распределения стекового кадра, введения динамической связи в стековый кадр и образования индикатора. Далее, перед каждой командой возврата компилятор должен генерировать код для освобождения стекового кадра и восстановления содержимого регистра BP. Эти действия обычно требуют много команд. Но точно такие же задачи решают команды ENTER (войти в процедуру) и LEAVE (выйти из процедуры). Команда LEAVE не имеет операндов, а в команде ENTER указываются два операнда. Вторым операндом является уровень вложения процедуры (самый внешний уровень соответствует 1). Он сообщает процессору, сколько элементов находится в индикаторе. Первый операнд показывает, сколько байт памяти требуется для переменных, объявленных в процедуре. Он сообщает процессору, сколько байт распределить для стекового кадра, исключая динамическую связь и индикатор. Например, команда ENTER 48,3 создает стековый кадр, содержащий динамическую связь, индикатор из трех указателей стековых кадров (один из них показывает на создаваемый стековый кадр) и 48 байт для переменных, объявленных в процедуре. Если второй операнд команды ENTER равен нулю, она не включает индикатор в стековый кадр. Это удобно для тех языков высокого уровня, которые не допускают вложенных процедур и не требуют индикатора. Примером такого языка служит язык Си. Типичные действия команды ENTER показаны на рис. 3.22. 102
"Формальное определение команды ENTER для всех случаев. LEVEL обозначает значение второго операнда. Включить в стек BP Установить временное значение FRAMEJPTR := SP if LEVEL > 0, then Repeat (LEVEL - 1) раз: BP := BP-2 Включить в стек слово, адресуемое BP End repeat Включить в стек FRAME-PTR End if BP := FRAME-PTR SP:« SP - первый операнд Рис. 3.22. Формальное опеределение команды ENTER 3.12. ЗАМЕЧАНИЯ О ПРЕФИКСАХ Мы рассмотрели все три префиксных байта - байты замены сегмента, повторения и блокировки. В связи с этим возникают следующие вопросы: можно ли в любой команде использовать любой префикс, можно ли указать перед командой несколько префиксов, что произойдет, если команда с префиксом прерывается. Любой префикс можно использовать с любой командой за одним исключением. Исключение составляет префикс повторения, который можно указывать только в цепочечных примитивах. Указание его перед любой другой командой вызовет особый случай недействительной операции. Префикс блокировки предназначен для определенных команд (MOVS, INS и OUTS) и заставляет процессор выдать сигнал LOCK на время их выполнения. Этот сигнал обычно применяется для обеспечения процессору монопольного доступа к памяти в мультипроцессорных системах. Однако разрешается ввести префикс блокировки и в любой другой команде, которая даже не обращается к памяти. Наконец, префикс замены сегмента можно использовать с любой командой. Если команда обращается к операнду в памяти, префикс показывает сегмент операнда; в противном случае он не действует. В общем, допускается применение префиксов в любой комбинации. Однако следует осторожно использовать вместе префиксы повторения и блокировки, так как это может запретить доступ к памяти другим процессорам на относительно продолжительное время, т.е. на все время выполнения повторяющейся цепочечной команды. юз
В заключение рассмотрим прерывания команд с префиксами. Прерывания чаще всего распознаются между командами. Но если заставить прерывание ожидать завершения повторяющейся цепочечной команды, ожидание может оказаться (относительно) продолжительным. Поэтому процессор допускает обслуживание прерываний после любого повторения цепочечной команды. Когда происходят повторения, указатель команды содержит смещение (первого префиксного байта) команды. Если команда прерывается, сохраняется именно это смещение и с него возобновляется выполнение после обработки прерывания. Следовательно, если кроме префикса повторения команда содержит и другие префиксы, они будут частью команды при ее возобновлении. В микропроцессоре 8086 этого не было. (Отметим, что возобновленная цепочечная команда не повторяет сделанного ею до прерывания; счетчик в регистре СХ и указатели в регистрах SI и DI модифицировались в каждом повторении до прерывания и возобновление выполнения команды начинается с этих модифицированных^начений.) 3.13. ВОЗДЕЙСТВИЕ НА ФЛАЖКИ Ранее мы говорили о воздействии на флажки некоторых команд, а здесь мы объединим всю информацию и полностью рассмотрим функционирование флажков. Флажки IOPL и NT относятся к механизму защиты процессора и обсуждаются в гл. 5. Остальные флажки можно разделить на флажки состояния и флажки управления. Первые из них показывают определенные свойства результатов команд, а вторые - управляют работой процессора. В табл. 3.22 показаны команды, результаты которых влияют на флажки состояния, и команды, служащие для задания состояния флажков управления. Попробуем разобраться в поведении некоторых флажков. Таблица 3.22. Воздействие команд на флажки А. Флажки состояния OF CF AF SF ZF PF ADD, ADC, SUB, SBB CMP, NEG, CMPS, SCAS INC, DEC 104 Сложение и вычитание + + + + + + + + + + + + Инкремент и декремент + - + + + +
Продолжение табл. 3.22 А. Флажки состояния OF CF AF SF ZF PF Умножение и деление MUL, IMUL + + ? ? ? ? DIV, IDIV ? ? ? ? ? ? Десятичная арифметика DAA, DAS ? + + + + + AAA, AAS ? + + ? ? AAM, AAD ? ? ? + + + Булевы команды AND, OR, XOR, TEST 0 0 ? + + + Сдвиг и циклический сдвиг SHL, SHR (единичный) + + ? + + + SHL, SHR (переменный) ? + ? + + + SAR 0 + ? + + + ROL, ROR, RCL, RCR (единичный) + + - - - - ROL, ROR, RCL, RCR (переменный) ? + — — — — Восстановление флажков POPF,IRET + + + + + + SAHF - + + + + + Установки флажка переноса STC 1 — — — CLC 0 — — — — CMC — * — Б. Флажки управления DF IF TF Восстановление флажков POPF,IRET + + + Прерывания INT, INTO - О О
Окончание табл. 3.22 Б. Флажки управления Dp IF TF Установки флажка направления STD 1 CLD О Установки флажка прерывания STI - 1 CLI - О Обозначения: + = изменяется, 1 = устанавливается в 1, О = сбрасывается в О, * ■ инвертируется, ? = не определен, — = не изменяется Команды сложения и вычитания воздействуют на все флажки состояния следующим образом: флажок переполнения OF и флажок переноса CF показывают, что знаковый или беззнаковый результат операции находится вне диапазона представимых чисел; флажок вспомогательного переноса AF показывает необходимость коррекции в десятичной операции; флажки знака SF, нуля ZF и паритета PF сигнализируют о том, что результат отрицательный, нулевой или содержит четное число единиц. К командам сложение и вычитания близки команды сравнения СМР, CMPS, SCAS и команда изменения знака NEG. Команды сравнения производят вычитание, и флажки отражают особенности результата вычитания. Команда NEG прибавляет 1 (после инвертирования всех бит), и флажки отражают результат этого сложения. Команда NEG устанавливает флажок CF в 1 в случае нулевого операнда, а флажок OF в случае, когда операнд равен -128 (8 бит) или -32 788 (16 бит). Команды инкремента и декремента воздействуют на флажки состояния так же, как команды сложения и вычитания, но они не влияют на состояние флажка переноса. Благодаря этому цикл арифметики многократной точности приобретает следующий вид: 1. Инициализировать регистр SI на смещение младшего байта первого операнда. 2. Инициализировать регистр DI на смещение младшего байта второго операнда. 106
3. Сбросить флажок переноса CF (команда CLC). 4. Прибавить с переносом (команда ADC) байт, адресуемый регистром SI, к байту, адресуемому регистром DL 5. Произвести инкремент (команда INC) содержимого регистра SI для указания наследующий старший байт первого операнда. 6. Произвести инкремент (команда INC) содержимого регистра DI для указания на следующий старший байт второго операнда. 7. Перейти к шагу 4, если операнды содержат дополнительные байты. Если бы команды INC (шаги 4 и 5) влияли на состояние флажка переноса, повторные выполнения команды ADC на шаге 4 не дали бы правильного результата. Команды умножения (отличные от IMUL с непосредственным операндом) образуют результат двойной длины, поэтому в установке флажков необходимо учитывать 32 бита. Поскольку результаты всех других команд не превышают 16 бит, в процессоре потребовались бы специальные схемы только для одной команды умножения. Кроме того, неясно, что делать с полученными состояниями флажков. Чтобы упростить процессор, после команды умножения состояние большинства флажков оставлено неопределенным. Это означает, что процессор не пытается воздействовать на флажки каким-то регулярным способом; он просто выполняет команду как можно проще и игнорирует флажки. Не исключено, что в следующих моделях процессора команда будет выполняться по-другому и давать другие состояния флажков. После выполнения команды умножения полезно знать, не вышло ли за диапазон произведение, рассматриваемое как число одинарной длины (как число двойной длины, оно никогда не выходит за диапазон). Это позволяет умножить байт на второй байт и прибавить произведение к третьему байту. Поэтому состояния флажков OF и CF не остаются неопределенными; они показывают, не выходит ли знаковое или беззнаковое произведение за диапазон, если его считать числом одинарной длины. Ради упрощения процессора после выполнения команды деления все флажки состояния не определены. Единственным флажком состояния, который важен после коррекции десятичного сложения и вычитания, является флажок переноса CF (он требуется в арифметике многократной точности); все остальные флажки можно оставить неопределенными. Однако микропроцессор 8080 имеет единственную десятичную команду DAA, которая воздействует на все пять флажков состояния (флажок переполнения в этом микропроцессоре отсутствует). Поэтому ради совместимости команда DAA процессоров 8086 и 80286 действует так же. По той же причине совместимости команды DAS, AAA и AAS должны воздействовать на пять флажков; команда DAS так и 107
делает, но трудности реализации заставили оставить неопределенными флажки SF, ZF и PF после команд AAA и AAS. Неясно, что должны обозначать флажки CF и AF после команд ААМ и AAD, поэтому они оставлены неопределенными. Поскольку булевы операции никогда не дают результат вне диапазона, после их выполнения флажки OF и CF сбрасываются в 0. Флажок AF не имеет никакого отношения к булевым операциям (он предназначен только для десятичной арифметики), поэтому его состояние не определено. Флажки SF, ZF и PF отражают особенности результата операции. Единственная булева операция NOT не воздействует на флажки. Это объясняется ошибкой при разработке микропроцессора 8086 и ради совместимости действие команды NOT оставлено таким же и в процессоре 80286. Команды сдвига осуществляют умножение и деление на степень числа 2. Флажки состояния отражают особенности результата, но с двумя исключениями: значение флажка AF не определено (сдвиг не имеет к десятичной арифметике никакого отношения) и значение флажка OF не определено для переменных сдвигов (в этом случае механизм обнаружения переполнения довольно сложен). Команда арифметического сдвига вправо SAR никогда не дает знаковый результат вне диапазона, поэтому после ее выполнения флажок OF сбрасывается в 0. Команды циклических сдвигов разработаны с учетом совместимости с аналогичными командами микропроцессора 8080 и аналогично воздействуют на флажки. Состояние флажка CF зависит от результата, а состояния флажков OF, SF, ZF и PF не изменяются. Ради совместимости было решено, что команды циклических сдвигов должны воздействовать на флажок OF аналогично командам сдвигов, хотя и непонятно, что в этом случае обозначает переполнение. Команды восстановления флажков возвращают в них ранее запомненные значения. В частности, команды POPF и IRET восстанавливают все флажки состояния и управления на значения, запомненные в стеке. Необычная команда SAHF (введенная только ради совместимости с микропроцессором 8080) передает во флажки состояния содержимое регистра АН. Все прерывания сбрасывают флажки TF и IF в 0. Если не сбрасывать флажок TF, процессор и в отладчике работал бы в пошаговом режиме. Сброс флажка разрешения прерываний IF позволяет процедуре прерывания определить, следует ли разрешить обработку второго прерывания (для чего применяется команда STI). 108
Смысл команд, воздействующих на флажки CF, DF и IF, очевиден. Они устанавливают в 1, сбрасывают в 0 или инвертируют отдельный флажок и не влияют на состояние остальных флажков. ЗАКЛЮЧЕНИЕ В этой главе рассмотрены базовые команды процессора 80286. Он не может производить вычисления с плавающей точкой, поэтому ему необходима поддержка численного процессора 80287. В следующей главе мы обсудим организацию процессора 80287 и его систему команд. ГЛАВА 4 ВЫЧИСЛЕНИЯ С ПЛАВАЮЩЕЙ ТОЧКОЙ И ПРОЦЕССОР 80287 4.1. ВВЕДЕНИЕ В предыдущих главах мы рассмотрели обширный набор арифметических команд процессора 80286 для работы с целыми числами. Однако многие задачи, особенно с привлечением физических величин, формулируются в терминах вещественных чисел, т.е. чисел с дробными частями. Вычисления с участием вещественных чисел (или их приближенных значений) называются численными вычислениями, соответствующий раздел математики называется численным анализом. Примерами численных вычислений служат научные расчеты, статистика, машинная графика, моделирование непрерывных процессов, системы численного управления, навигация, управление технологическими процессами. Здесь требуется, чтобы компьютер мог представлять и обрабатывать вещественные числа. Можно представить вещественные числа, полагая, что в 16-битном слове существует двоичная точка (аналогичная десятичной точке). Например, вместо интерпретации слова 0000000 01110001 как 11100012 « 11310 можно считать, что после третьего разряда справа находится двоичная точка и представить слово как 1110.0012 = 14.12510. При этом арифметические команды процессора 80286 будут давать правильные результаты, если во всех операндах (подразумеваемые) двоичные точки фиксированы в одном и том же месте. Такое соглашение называется представлением с фиксированной точкой. Но здесь сразу же возникает проблема: диапазон чисел от максимального до минимального очень мал. Если двоичную точку поместить после седьмого разряда справа (для десятичных чисел это всего три разряда), то максимальное представимое число равно всего 29 - 1 = 511. Если требуют- 109
ся большие числа, придется уменьшать число разрядов справа от точки, так как позиция двоичной точки должна быть фиксирована для всех чисел. Одно из решений проблемы связано с масштабированием (неявным умножением на масштабный коэффициент) каждого числа, чтобы оно оставалось в диапазоне. Этот способ оказывается довольно сложным, так как промежуточные результаты по величине могут значительно отличаться от исходных данных и окончательных результатов. Например, в операциях интегрирования и дифференцирования приходится как-то аппроксимировать такие понятия, как "бесконечно малая величина" и "бесконечность". Однако трудности возникают и в таких кажущихся простыми задачах, как решение системы линейных уравнений. Хотя ручное масштабирование до сих пор применяется в тех з&дачах, где требуется высокая производительность, а диапазоны чисел известны, оно связано с трудностями программирования, так как нужно заставить компьютер следить за масштабами. Для этого к каждому числу присоединяется некоторый индикатор масштаба. Если ограничиться масштабными коэффициентами, являющимися степенями числа 2, то это эквивалентно указанию с каждым числом второго числа, показывающего положение двоичной точки в первом числе. Позиция двоичной точки может изменяться от числа к числу, т.е. она начинает "плавать". Таким образом, мы подходим к представлению чисел с плавающей точкой. Процессор 80286, как и его предшественник микропроцессор 8086, не имеет команд, оперирующих числами с плавающей точкой. Конечно, можно разработать библиотеку подпрограмм, реализующих различные арифметические операции. Однако операции над числами с плавающей точкой можно значительно ускорить, если применить специализированные схемы. Для этого фирма Intel предлагает численный процессор 80287. Микросхема 80287 выполняет операции с плавающей точкой в 50 - 100 раз быстрее эквивалентных подпрограмм процессора 80286. Программисты могут включать команды 80287 в программы для процессора 80286 так, как будто они являются командами процессора 80286. Когда процессор 80286 встречает команду процессора 80287, он передает ее для выполнения этому процессору. Возникает иллюзия того, что процессор 80286 может выполнять команды с плавающей точкой. Точный механизм взаимодействия процессоров, незаметный для программиста, рассмотрен в гл. 6. А что произойдет, если процессор 80286 встречает команду процессора 80287, а численного процессора в системе нет? В этом случае процессор 80286 передает управление подпрограмме обработки особого случая, указанной операционной системой (подробнее см. гл. 5). Фирма Intel поставляет подпрограмму Е80287, которая программно эмулирует дейст- 110
вия процессора 80287. Следовательно, эта подпрограмма создает иллюзию наличия в системе процессора 80287 и выполнения процессором 80286 команд с плавающей точкой. Обычная программа (не входящая в состав операционной системы) не может уловить различий между эмулирующей программой и процессором 80287, но операции с плавающей точкой выполняются в 50 - 100 раз медленнее. Таким образом, производители микрокомпьютеров могут продавать базовые системы без процессора 80287, а затем поставлять его для повышения производительности. Далее мы обсудим форматы чисел с плавающей точкой, а затем рассмотрим архитектуру процессора 80287, включая его регистры и команды. Кроме того, будут даны примеры программирования и описаны необычные возможности этого процессора. 4.2. ФОРМАТЫ ЧИСЕЛ С ПЛАВАЮЩЕЙ ТОЧКОЙ Представление чисел с плавающей точкой изучается в старших классах школы под названием "научная нотация". Например, число -1234000 можно представить как -1.234 х Ю6. Здесь мы видим три части числа: знак "минус", мантиссу 1.234 и порядок 6. Каждое число имеет множество представлений в научной нотации, например: 13.2 X Ю4 = 1.32 X 105 = 0.132 х Ю6 = 0.0132 х Ю7 Чтобы избежать неоднозначности, для каждого числа можно выбрать одно из представлений и назвать его нормализованным. Тогда все остальные представления будут ненормализованными. В частности, можно всегда выбрать представление точно с одной ненулевой цифрой слева от десятичной точки в мантиссе (в примере - это 1.32 х Ю5). Такой выбор очень удачен, поскольку не требует записывать старшие нули (например, 0.0000132) в мантиссе. Следовательно, у каждого числа появляется единственное нормализованное представление. Изложенные принципы относятся и к представлению чисел с плавающей точкой в процессоре 80287. Различие заключается только в том, что мантисса и порядок являются двоичными числами, а не десятичными, и порядок определяет степень числа 2, а не 10. На рис. 4.1 показаны три формата чисел с плавающей точкой, распознаваемые процессором 80287: они соответствуют одинарной, двойной и расширенной точности. В некоторых материалах фирмы Intel они называются также коротким вещественным, длинным вещественным и временным вещественным. Каждый формат состоит из трех полей: знак, порядок и мантисса. Числа в этих форматах занимают в памяти 4,8 или 10 байт. ш
1 бит 8 бит 23 бита Одинарная точность S I Порядок Мантисса 4 байта 1 бит 11 бит 52 бита Двойная точность S Порядок Мантисса 8 байт 1 бит 15 бит 64 бита Расширенная точность S Порядок I Мантисса 10 байт Рис. 4.1. Форматы чисел с плавающей точкой Напомним, что у 16-битных целых чисел процессора 80286 младший (правый) байт хранится в памяти по меньшему адресу, а старший (левый) байт - по большему адресу. Следовательно, по мере перехода к большим адресам мы встречаем величины все большей значимости. Все 16-битное целое число адресуется путем указания адреса младшего байта. Аналогичный способ применяется и для всех чисел с плавающей точкой. Байт с наименьшим адресом является младшим байтом мантиссы. При переходе к большим адресам мы встречаем все более значимые части мантиссы, затем младшую часть порядка, старшую часть порядка и, наконец, байт с наибольшим адресом содержит семь старших бит порядка и однобитное поле знака. Отметим, что упорядочение полей числа с плавающей точкой совместимо с тем принципом, что более важные или значимые части числа должны иметь большие адреса. Поле знака наиболее важно при сравнении значений двух чисел с плавающей точкой. Затем по важности следует порядок, а мантисса наименее важна. Рассмотрим поля чисел с плавающей точкой подробнее. Поле знака. Поле знака представлено одним битом, причем 0 показывает положительное число, а 1 - отрицательное. При переходе от положительных чисел к отрицательным изменяется только знаковый бит; дополнительный код здесь не применяется. А как выглядит нуль в формате с плавающей точкой? Любое число с плавающей точкой, содержащее нули в полях порядка и мантиссы, считается нулем. Следовательно, появляются два нуля: +0 и -0. Разработчики процессора 80287 скрыли этот факт от программистов. Например, в командах сравнения оба нуля считаются одним и тем же числом. Об этом должны помнить программисты, которые применяют последовательность (более быстрых) команд целочисленного сравнения процессора 80286 для моделирования (более медленного) сравнения чисел с плавающей точкой процессора 80287. 112
Поле мантиссы. Мантиссы чисел с плавающей точкой имеют вид: Х.ХХХХХХХ... X X = 0 или 1 Г двоичная точка Для получения наибольшей точности (т.е. максимизации числа значащих бит в мантиссе) мы требуем, чтобы числа с плавающей точкой были нормализованными. Порядки необходимо скорректировать так, чтобы левый (старший) бит мантиссы был не 0. При этом нет потери точности из-за хранения старших нулей. Например, вместо мантиссы 0.0001011 ... 11 следует уменьшить порядок на четыре и использовать мантиссу 1.011 ... 110000. В двоичной системе счисления требование того, что старшая двоичная цифра мантиссы должна быть ненулевой, эквивалентно требованию того, чтобы она была 1 (что не справедливо, если основание отличается от числа 2). Поэтому все мантиссы представляются в форме: 1.ХХХХХХХ ...X Но если старший бит всегда содержит 1, ее можно не хранить в каждом числе с плавающей точкой. Поэтому ради дополнительного бита точности процессор 80287 хранит числа одинарной и двойной точности без старшего бита мантиссы. Следовательно, невозможно иметь число с одинарной или двойной точностью, не являющееся ненормализованным. Если, например, поле мантиссы такого числа содержит биты ООП ... 101, то мантисса фактически равна 10011 ... 101. Мы поясним слово "невозможно" позднее, когда будем говорить об ошибочных значениях; пока отметим, что кодирование нуля (нулевые поля мантиссы и порядка) является исключением из нашего правила. По ряду причин способ с неявным битом относится только к числам одинарной и двойной точности, но не к числам с расширенной точностью. Во-первых, числа с явным старшим битом обрабатываются несколько быстрее (все числа внутри процессора 80287 представлены в расширенном формате). Во-вторых, число расширенной точности уже имеет достаточную точность. Наконец, мы коснемся неявного бита при рассмотрении обработки ошибок. Поле порядка. Поле порядка определяет степень числа 2, на которую нужно умножить мантиссу для получения значения числа с плавающей точкой. В некоторых компьютерах, например IBM 360/370, вместо степеней числа 2 применяются степени числа 16. Для процессора 80287 выбраны степени числа 2 потому, что они позволяют ввести скрытый бит и обеспечивают максимальную производительность при заданном объеме аппаратных средств. из
Чтобы иметь отрицательные порядки, в поле порядка находится сумма истинного порядка и положительной константы, называемой смещением. Смещение равно 127 для одинарной точности, 1023 - для двойной точности и 16383 - для расширенной точности. Если, например, истинный порядок числа с одинарной точностью равен +5, то в поле порядка находится значение 127 + 5 = 132; если истинный порядок равен -5, то в поле порядка будет 127 - 5 = 122. В формате одинарной точности длина поля порядка 8 бит и наибольшее целое равно 28 - 1 = 255. Следовательно, наибольший истинный порядок равен 255 - 127 = 128, а наименьший - 0- 127 = -127. Наибольший и наименьший порядки зарезервированы для обработки ошибок, поэтому фактически наибольший допустимый истинный порядок равен 127, а наименьший -126. Аналогичные рассуждения относятся к числам с двойной и расширенной точностью. Значения чисел с плавающей точкой. Теперь у нас достаточно информации для того, чтобы записать соотношения для значений чисел с плавающей точкой в терминах содержимого различных полей; (- 1г (1. fj2 ••• /23)х 2 (одинарная точность), (-1)5*" (1. fj2 ... /52) х 2*Е ~ 1023^ (двойная точность), (-1)^"(/х./2... /б4) х 2(Е ~ 16383) (расширенная точность), где sgn - значение, находящееся в знаковом бите; Д/2 ... - биты, хранимые в поле мантиссы; б - значение, содержащееся в поле порядка. Например, число с одинарной точностью 1 01111110 11000000000000000000000 имеет sgn = 1, Е = 126, fl = 1, /2 = 1. Его значение равно (-1)1 (1,112) X 2(126 "127) = -1(1.75) X 2-1 = -0,875. Параметры чисел с плавающей точкой. Разобравшись с внутренней структурой чисел с плавающей точкой, обсудим, почему введены три формата таких чисел. В табл. 4.1 показаны десятичные диапазон и точность этих форматов. Тридцатидвухбитный формат одинарной точности аналогичен соответствующему формату большинства компьютеров. Отметим, что он гарантирует точность всего в 6 десятичных цифр и не может абсолютно точно представить умеренное число 9999999. Следовательно, целесообразно принять в качестве формата "по умолчанию" формат двойной точности, чтобы избежать накопления ошибок, и пользоваться форматом одинарной точности только при ограниченной памяти. Так как процессор 114
80287 преобразует все числа в формат расширенной точности, операции над числами с двойной точностью выполняются не намного медленнее, чем над более короткими числами. Таблица 4.1. Гарантированные десятичные диапазон и точность чисел с плавающей точкой Формат Значащих цифр Наименьшая степень числа 10 Наибольшая степень числа 10 Одинарный б -37 38 Двойной 15 -307 308 Расширенный 19 -4931 4932 Шестидесятичетырехбитный формат двойной точности незначительно отличается от соответствующего формата других компьютеров. В некоторых из них числа с одинарной и двойной точностью имеют поля порядков одинаковой длины, но в процессоре 80287 поле порядка увеличено с 8 бит до 11. Одно из преимуществ одинаковых полей порядков заключается в том, что число с двойной точностью можно быстро преобразовать в число с одинарной точностью путем простого отбрасывания 4 младших байт, а обратное преобразование заключается в добавлении 4 младших нулевых байт. Но, с другой стороны, увеличение длины порядка с 8 до И бит гарантирует получение произведения двух чисел с одинарной точностью без потери точности и возможности ошибки. Отметим, что мы уже встречались с таким желательным свойством умножения в целочисленной арифметике (см, команду MUL процессора 80286). Наконец, мы подошли к 80-битному формату расширенной точности, которого нет в большинстве других компьютеров. Впервые он появился в микропроцессоре 8087. Этот формат предназначен для представления промежуточных результатов, чтобы упростить получение точных окончательных результатов в формате двойной точности. Представлять в формате расширенной точности окончательные результаты не рекомендуется. Благодаря этому формату с его огромными диапазоном и точностью даже неопытные в области численных расчетов программисты могут запрограммировать задачи, требующие тщательного анализа точности и надежности результатов. Чтобы показать необходимость расширенной точности, рассмотрим вычисление показательной функции. Функция z = xy важна в том отношении, что из большинства распространенных функций, встречающихся в языках программирования, ее наиболее трудно вычислить точно. Каза- 115
лось бы, при точных значениях х и у, представленных в формате двойной точности, функция z = в этом же формате должна быть точной. Однако это очень трудно гарантировать без привлечения формата расширенной точности для хранения промежуточных результатов. Для доказательства попробуем вычислить z по стандартной формуле Здесь z является окончательным результатом, а у log2x- промежуточным. Разложим промежуточный результат на дробную и целую части: ylog2(x) = I + F, где I - целая часть у log2(x), a F - дробная. Число с плавающей точкой, в котором хранится этот промежуточный результат, должно иметь достаточно бит в мантиссе для представления чисел I и F. Чтобы определить требуемое число бит, найдем, сколько их нужно для числа I и сколько - для F, а затем сложим результаты. Будем исходить при этом из окончательного результата. Его можно записать в виде: Поскольку число 2 в дробной степени всегда находится между 1 и 2, видно, что фактически 2 F есть мантисса в нормализованном представлении числа z, а I - его истинный (несмещенный) порядок. Так как результат z занимает весь диапазон чисел с двойной точностью (ведь если у = 1, то z — х), целая часть I требует 11 бит, a F - 53 бита (нормализованные числа с двойной точностью имеют поле порядка 11 бит, поле мантиссы 52 бита и один неявный бит). Следовательно, промежуточный результат I + F требует мантиссы с длиной минимум 11 + 53 = 64 бита. Поэтому неслучайно, что формат расширенной точности обеспечивает длину мантиссы 64 бита. У вас наверняка возникнет вопрос: "Почему размер числа с расширенной точностью составляет 10 байт, а не равен какой-нибудь степени числа 2, как принято в мире компьютеров?" Ведь, например, индексирование массивов 10-байтных величин требует (медленного) умножения на 10 вместо (быстрого) сдвига влево. Программисты, более заинтересованные в скорости, чем в экономии памяти, могут отвести 16 байт для каждого числа с расширенной точностью, считая лишние 6 байт пустыми ("заполнителями"). Почему же разработчики процессора 80287 не остановились на 16 байтах? Одна из причин заключается в том, что результатом были бы более сложные схемы процессора или меньшая производительность при том же объеме аппаратных средств. Еще одно объяснение вытекает из противоречия, присущего целям расширенной точности. Она предназначена для того, чтобы защитить промежуточные результаты, Пб
получающиеся при вычислениях функций с числами, имеющими двойную точность. Но если обнаруживается, что числами с расширенной точностью пользоваться легко, то программисты попытаются делать все вычисления с расширенной точностью. Теперь потребуется расширение расширенной точности для защиты промежуточных результатов таких вычисле ний и т.д. Диапазоны чисел с плавающей точкой. Диапазоны трех форматов чисел с плавающей точкой показаны на рис. 4.2. Этот рисунок иллюстрирует несколько интересных особенностей арифметики в сопроцессоре 80287. Если результат арифметической операции меньше наименьшего отрицательного числа или больше наибольшего положительного числа конкретного формата, то говорят, что операция вызвала переполнение. Если же результат арифметической операции ненулевой, но находится между наибольшим отрицательным и наименьшим положительным числами, то говорят, что в операции возникло антипереполнение. Наименьшее отрицательное Наибольшее отрицательное Наименьшее положительное Наибольшее положительное Одинарная точность Двойная точность Расширенная г точность I | 1| 1 ...ю | 1. .1 I 1| 0...01 | 0...0 | hi 0...01 | 0...0 Ihl 11...10 | 1...1 | -3.37x1038 -1.17Х10-38 1.17 x 10 -38 3.37 X1038 1| 1.10 | 1. .1 j | 11 0...01 | 0...0 | h 0...01 | 0...0 Ihl 11...10 | 1...1 | -1.67 хЮ308 -2.23 х КГ308 2.23 хЮ"** 1.67 xlO308 | 1| 1 .10 ) 1. .1 I 1 | 0...01 | 0...0 | h 0...01 | 10...0 Ihl 11...10 | 1...1 | -1.2 хЮ4932 -3.37 x Ю-*932 3.37 хЮ"4*32 1.2 xlO4*32 Во всех форматах | 1 j 0...0 | 0.0 | | o| 0...0 | 0...0 | -0 40 Рис. 4.2. Диапазоны нормализованных чисел с плавающей точкой Отметим, что рис. 4.2 абсолютно симметричен для положительных и отрицательных чисел. Следовательно, операция нахождения абсолютного значения никогда не может вызвать ни переполнения, ни антипереполнения. Такое свойство не соблюдается, например, в целочисленной арифметике процессора 80286. Обратимся теперь к положительным числам. Если умножить наименьшее положительное число на наибольшее положительное число, то результат во всех форматах будет равен примерно 4. Другими словами, наибольшее положительное число примерно в 4 раза больше обратной величины наименьшего числа. Следовательно, такие часто встречающие- 117
ся операции, как 2/х, 3/х и л/х, не могут вызвать переполнения, но могут привести к антипереполнению. Однако процессор 80287 может устранить такие антипереполнения и уменьшить их влияние (для приведенных выше операций), что приведет к потере в худшем случае двух бит точности. Об этом мы поговорим далее. Значения особых случаев. При выполнении арифметических операций над числами с плавающей точкой иногда возникают ошибочные условия или особые случаи. Самым известным из них является деление на нуль, но существуют и другие. Ниже дан список тех видов численных особых случаев, которые могут возникать в процессоре 80287, упорядоченный по степени тяжести их последствий. Неточный результат. Это условие возникает, когда результат операции невозможно точно представить в формате приемника. Например, при делении 1.0 на 3.0 получается бесконечная двоичная дробь 0.0101010101 ..., которую невозможно точно представить ни в одном формате с плавающей точкой. Такая же ситуация возникает, если число с двойной точностью 9.87654321 преобразуется в формат числа с одинарной точностью, не имеющий разрядов для всех девяти значащих цифр. Большинство пользователей не должно считать неточный резу.тьтат ошибкой, а полагать, что имеет место автоматическое округление или усечение. Численное антипереполнение. Как мы уже говорили, эта ситуация возникает, когда ненулевой результат по абсолютной величине слишком мал для представления, т.е. когда он слишком близок к нулю. Примером служит результат деления 1.0 на наибольшее число с расширенной точностью. Другой пример - преобразование наименьшего положительного числа с расширенной точностью в число с двойной точностью. Применение расширенной точности для всех промежуточных результатов делает этот особый случай маловероятным. Деление на нуль. Особый случай деления на нуль возникает, когда ненулевое число делится на нуль. Численное переполнение. Как уже говорилось, этот особый случай возникает, когда результат слишком велик по абсолютной величине, чтобы быть представленным в формате приемника. Примерами служат сложение наибольшего числа с расширенной точностью с самим собой или преобразование такого числа в число с двойной точностью. Как и антипереполнение, этот особый случай маловероятен при использовании для всех промежуточных результатов расширенной точности. Недействительная операция. Особый случай недействительной операции включает в себя все, что осталось, например деление 0 на 0, извлечение корня квадратного из отрицательного числа или попытку использовать несуществующий регистр процессора 80287. 118
Еще один особый случай денормализованного операнда мы рассмотрим позже. Обычная обработка особых случаев в компьютерах заключается в том, чтобы немедленно приостановить операцию и передать управление процедуре обработки особого случая. Такой метод называется специальным прерыванием ("ловушкой"). Второй метод заключается в том, чтобы заставить операцию возвратить специальное значение особого случая1 и продолжить вычисления. Он редко применяется в нечисленном программировании по следующим причинам: 1. Чтобы пользователь мог обнаружить возникновение особого случая, его специальное значение должно отличаться от обычных значений. Кроме того, для разных особых случаев должны существовать различные специальные значения. Однако часто трудно получить лишние биты, необходимые для отметки специальных значений. Например, программисты вряд ли согласятся уменьшить диапазон 8-битных целых чисел, чтобы ввести несколько специальных значений. 2. Когда программист кодирует условный переход по результату сравнения, он должен тщательно проверить, как программа отреагирует, если одним из операндов в команде сравнения окажется специальное значение. Это весьма усложняет программу. 3. При отладке программы программист работает с ее текстом "назад" от точки, где обнаружена ошибка, к команде, которая вызвала ошибку. Чем больше расстояние между ошибкой и местом ее проявления, тем труднее найти ошибку. Если разрешить продолжение выполнения программы после возникновения ошибки, отладка намного усложнится. В вычислениях над числами с плавающей точкой ситуация оказывается иной. 1. В процессоре 80287 специальные значения в отличие от обычных чисел с плавающей точкой имеют порядки ООО ... 00 или 111 ... 11 (т.е. наименьшее и наибольшее значения в каждом формате), что мы уже отмечали. Сокращение диапазона представимых чисел невелико. 2. Проверки чисел с плавающей точкой встречаются реже нечисленных проверок. Более того, сравнения чисел с плавающей точкой на равенство или неравенство выполняются очень редко. Наборы для специальных значений выбраны так, чтобы большинство сравнений выполнялось правильно, даже если программист игнорирует специальные значения. Далее мы будем пользоваться более коротким термином специальное значение. - Прим. перев. 119
3. Даже если программа получает специальные значения для некоторых промежуточных результатов, возможно все же, что она даст правильный окончательный результат. Это объясняется тем, что вычисления над числами с плавающей точкой являются приближенными. Следовательно, можно разрешить продолжать вычисления до тех пор, пока это возможно. Специальные значения можно использовать для разработки программ, устойчивых к появлению особых случаев. С другой точки зрения, специальные значения просто увеличивают диапазоны представления форматов с плавающей точкой. Здесь они считаются отнюдь не ошибочными, и знающие их программисты могут намеренно использовать их для упрощения своих программ. Вот почему термин специальное значение лучше термина ошибочное значение. Процессор 80287 разрешает программисту выбирать между прерываниями при возникновении особого случая и получением специальных значений. Прерывания по каждому из приведенных выше особых случаев можно индивидуально разрешать или маскировать (т.е. запрещать). Маскирование одного вида особого случая может быть рискованнее, чем маскирование другого в том смысле, что в программе неопытного пользователя появятся ошибки, когда возникнет специальное значение. Ниже мы рассмотрим специальные значения, формируемые для каждого вида особого случая. Описания вновь упорядочены по степени увеличения риска. Особый случай неточного результата. Если особый случай неточного результата замаскирован, процессор округляет результат до обычного числа с плавающей точкой вместо возвращения специального значения. Программист может выбирать из четырех режимов округления, показанных на рис. 4.3. Крестиками на числовой оси обозначены бесконечно точные вещественные числа, являющиеся истинными результатами некоторых арифметических операций. Большие точки показывают ближайшие представимые числа с плавающей точкой с обеих сторон истинного результата. При округлении в направлении к нулю или усечении (отбрасывании) истинный результат приводится в формат приемника путем игнорирования достаточного числа младших бит (рис 4.3, а). Это эквивалентно выбору ближайшего числа с плавающей точкой, которое по абсолютному значению меньше истинного результата. Такой режим округления наиболее удобен в целочисленной арифметике, где, например, желательно округлить 3/2 до 1, а -3/2 до -1. За исключением задач моделирования целочисленной арифметики нам хочется минимизировать ошибку округления, т.е. лучше всего округлять истинный результат до ближайшего числа с плавакщей точкой. Такой способ называется округлением к ближайшему (см. рис. 120
Отрицательные числа Положительные числа (угу » f\fi а) • МИ • • • И«И • " б)м» • нам • • • тм • (уг> * /ул в) • • ••К«М« • • я*я •• • • г> • • • • П9Ш • • • И» И • • т • Рис. 4.3. Режимы округления 4.3,5). Если истинный результат находится точно посередине между парой чисел с плавающей точкой, выбирается четное число, т.е. то число пары, которое имеет 0 в младшем бите мантиссы. В некоторых компьютерах всегда производится округление вверх, но оно вносит в сложных расчетах значительное смещение. Чтобы показать это, воспользуемся десятичными числами с плавающей точкой, имеющими пятиразрядные мантиссы. При округлении вверх будем получать: а = 1.00000 Ь- 0.55555 а+Ъ =1.5556 -Ь =i.oooi +Ъ-Ъ =1.0002 +Ъ-Ъ = 1.0003 Результат все время растет несмотря на то, что мы прибавляем и вычитаем одно и то же значение. Этот неустойчивый дрейф исчезает, если при вычислении той же суммы применяется округление к четному: а+Ъ =1.5556 -Ь = 1.0000 +Ъ-Ъ =1.0000 +Ь-Ь = 1.0000 121
Наиболее благоприятное округление к ближайшему принято в процессоре 80287 по умолчанию, а остальные режимы округления должны применяться только для специальных целей. На рис. 4.3,в и г показаны еще два режима округления: округление о направлении к положительной бесконечности и округление в направлении к отрицательной бесконечности. Они используются для реализации интервальной арифметики. Программа с применением обычной арифметики с плавающей точкой дает результат, который является только приближенным значением истинного результата. Требуется сложный ее анализ, чтобы в общем сказать, насколько хорошим оказывается приближение. Однако существует способ, который не требует анализа ошибок округления. В нем каждая арифметическая операция выполняется дважды: один раз с округлением в направлении к отрицательной бесконечности, а второй - к положительной. ТогДа гарантируется, что истинный результат операции находится между двумя полученными результатами. К сожалению, обычно невозможно получить границы истинного результата всей программы, выполнив ее два раза с двумя направлениями округления. Например, умножение на отрицательное число превращу ет верхнюю границу в нижнюю (и наоборот). Кроме того, нужно тщательно проанализировать условные переходы по результатам сравнений чисел с плавающей точкой. Особый случай численного антипереполнения. Здесь мы встречаем первое специальное значение. Напомним, что наименьшее значение и поле порядка ООО... 00 пока еще не использовалось. Напомним также, ш$ в числах с одинарной и двойной точностью старший бит мантиссы на хранится, а всегда подразумевается равным 1. Можно принять соглашение о том, что когда поле порядка содержит ООО ... ОС^мы будем считать его равным ООО ... 01 (наименьшее обычное поле порядка) и полагать, шо старший бит мантиссы равен 0, а не 1. Такой прием расширяет возможность представления очень мапых чисел. В числах с одинарной точностью смещение порядка равно 127, поэтому поле порядка 00000001 соответствует истинному порядку -126. Мы будем считать, что поле порядка 00000000 соответствует истинному порядку -126, но с неявным старшим битом мантиссы, равным 0. Рассмотрим, например, следующие числа с одинарной точностью: 0 00000001 10000000000000000000000 обозначает 1.12 х 2 ~126 0 00000001 00000000000000000000000 обозначает 1.0 Х2~126 0 00000000 10000000000000000000000 обозначает 0.1* х 2 ~126 0 00000000 01000000000000000000000 обозначает 0.12 X 2 ~127 Отметим, что два последних числа слишком малы для представления обычными числами с плавающей точкой. Конечно, допуская стармшн 122
нули в мантиссе, мы не имеем нормализованного числа. По существу, мы используем старшие нули мантиссы для искусственного расширения порядков в область более отрицательных значений за счет потери точности мантиссы, что наглядно видно в последнем из приведенных выше чисел. Этот прием позволяет расширить порядок одинарной точности от -126 до -149, но при этом точность уменьшается до одного бита. Числа с плавающей точкой, имеющие в поле порядка число ООО ... 00, называются денормализованными. Ради совместимости форматов мы введем это соглашение и для чисел с расширенной точностью, хотя они имеют явный старший бит мантиссы. Следовательно, поля порядков чисел с расширенной точностью, содержащие 000000000000001 и 000000000000000, соответствуют истинному порядку -16382. Если особый случай численного антипереполнения замаскирован, процессор 80287 реагирует на него образованием денормализованного числа. Если же результат настолько мал, что его нельзя представить и таким числом, формируется нуль. Такой способ называется постепенным (плавным) антипереполнением. В большинстве компьютеров на все замаскированные антипереполнения формируются нули, т.е. в них реализовано резкое антипереполнение. Принятый в процессоре 80287 способ оказывается лучше. Рассмотрим, например, вычисление (у-х) + х, где разность (у - х) вызывает антипереполнение. Резкое антипереполнение возвращает как результат х, а плавное - правильный результат у. В этом примере денормализованное число (у - х) является операндом операции сложения. Ниже мы подробно рассмотрим эту ситуацию и введем еще один вид особого случая. Особый случай денормализованного операнда. Этот особый случай возникает, когда операндом команды оказывается денормализованное число. Прерывание по такому особому случаю применяется при моделировании на процессоре 80287 других режимов арифметики. Большинство программистов его не касаются, поэтому, если они маскируют особый случай численного антипереполнения и допускают этим самым появление денормализованных чисел, они, по-видимому, замаскируют и особый случай денормализованного операнда и разрешат использовать денорма- лизованные числа в операциях. Поэтому рассмотрим, что происходит, когда особый случай денормализованного операнда замаскирован. Денормализованные числа появляются, когда ради увеличения диапазона приходится жертвовать точностью. Процессор 80287 пытается не допустить, чтобы худшая точность денормализованных чисел ухудшала точность остальных вычислений, не давая никаких предупреждений. Предупреждение может быть в форме прерывания или специального 123
значения. Таким образом, операции с привлечением денормализованных операндов: 1) дают нуль, если результат слишком мал для представления его денормализованным числом (например, произведение двух денормализованных чисел); 2) дают денормализованный результат, если результат достаточно мал для представления его денормализованным числом (например, сумма двух малых денормализованных чисел); 3) дают нормализованный результат, если неточность денормализованного операнда влияет на результат меньше, чем ошибка округления (например, сумма денормализованного числа и большого нормализованного числа); 4) дают ненормализованный результат (см. ниже), если результат неточный, но слишком велик для денормализованного числа, а приемник результата имеет расширенную точность (например, произведение денормализованного числа на большое нормализованное число); 5) вызывают особый случай недействительной операции, если результат удовлетворяет условию 4, а его приемник не имеет расширенной точности. Кроме того, все попытки деления на денормализованное число или извлечения корня квадратного из денормализованного числа вызывают особый случай недействительной операции. Ненормализованное число - это число с расширенной точностью, имеющее обычное поле порядка (т.е. не ООО ... 00 или 111 ... 11), но нулевой старший бит мантиссы. Такие числа существуют только в формате расширенной точности, так как в других форматах нет явного старшего бита мантиссы. Ненормализованные операнды подчиняются тем же правилам 1 - 5, что и денормализованные числа. Особый вид ненормализованного числа называется псевдонулем; он появляется, когда умножаются два ненормализованных числа, имеющих в своих мантиссах суммарно более 64 старших нулей. Псевдонуль имеет ненулевое поле порядка и нулевую мантиссу. Псевдонули ведут себя так же, как ненормализованные числа, но в сравнениях они считаются нулями. Правила 1-5 гарантируют, что все "последователи" денормализованных чисел либо отмечаются как подозрительные (денормализованные числа, ненормализованные числа, нули), либо имеют малую потерю точности. Особый случай деления на нуль. Напомним, что мы еще не использовали наибольший код в поле порядка 111 ... 11. Число с плавающей точкой, имеющее единицы в поле порядка и нули в поле мантиссы, называется бесконечностью. Так как знаковый бит бесконечности может содержать + или - , появляются две бесконечности: + 00 и - 00. Бесконеч- 124
ности возникают, когда особый случай деления на нуль замаскирован, но выполняется деление на нуль, или когда особый случай численного переполнения замаскирован и возникает переполнение. Рассмотрим вначале деление на нуль. Напомним, что имеется два нуля: +0 и -0, которые очевидным образом приводят к знаковым бесконечностям: х/+0 = +°°, -х/+0 = х/-0 = = -х/-0 = +°°, где х больше нуля и не бесконечность. Что происходит, если бесконечности используются в качестве операндов? В умножении и делении появляются ожидаемые результаты: где х по-прежнему больше нуля и не бесконечность. Операции умножения и деления с операндами бесконечность и нуль вызывают особый случай недействительной операции; он же возникает и при делении бесконечности на бесконечность. При рассмотрении операций сложения, вычитания и сравнения появляются трудности. Процессор 80287 имеет два режима управления бесконечностью: проективный и аффинный. В проективном режиме факт наличия двух бесконечностей скрыт (как скрыт факт наличия двух нулей), что показано на рис. 4.4,а. В аффинном режиме распознаются две бесконечности и два нуля; здесь все конечные числа меньше +°° и больше -°° (см. рис. 4.4,6). Обратимся вначале к проективному режиму. В проективном режиме сравнение бесконечности с конечным числом вызывает особый случай недействительной операции, а сравнение двух бесконечностей всегда дает ответ "равны". С другой стороны, сложение и вычитание двух бесконечностей вызывает особый случай недействительной операции, а сложение и вычитание бесконечности и конечного числа возвращает бесконечность. Отрицатель-i ные числа Положительные числа Отрицательные Положитель- числа ные числа •О ♦ <> б) о а) Рис. 4.4. Проективная и аффинная бесконечности: a — проективное замыкание, б — аффинное замыкание 125
Режим проективной бесконечности удобен для вычисления рациональных функций (значения в полюсах можно представить бесконечностями) и цепных дробей. Предположим, например, что необходимо вычислить следующую рациональную функцию для многих значений х: „ ч 2х +х2 +х + Ъ х - х + 4 Можно оптимальным образом вычислить каждый полином в отдельности, а затем разделить: Л) ((2х + \)х + \)х + 3 ПХ)~ (х-\)х + 4 Здесь придется выполнить 4 умножения, одно деление и 5 сложений / вычитаний. Второй способ заключается в преобразовании f(x) в цепную дробь: f(x) = (2x + 3)~ (х- 13,4), 181/16 (х + 9/4) Получается одно умножение, два деления и 5 сложений / вычитаний. Числа 13/4, 9/4 и 181/16 являются константами. Цепные дроби уменьшают общее число умножений и делений за счет увеличения числа делений. Это выгодно, когда деления не намного сложнее умножений. В процессоре 80287 операция деления с двойной точностью длится примерно в 1,5 раза дольше, чем операция умножения с двойной точностью. В нашем примере программа для цепной дроби выполняется примерно в 1,1 раза быстрее исходной программы. Если бы в программу для цепной дроби добавить явную проверку х = -9/4, чтобы избежать пугающего деления на нуль, она стала бы сложнее и выполнялась бы дольше. Аффинный режим либеральнее проективного в том отношении, что он допускает больше операций над бесконечностями. Кроме операций проективного режима аффинный режим разрешает сложение бесконечностей с одинаковыми знаками и вычитание бесконечностей с разными знаками, давая очевидные результаты, например (+ °°) + (+ °°) = + °° и (- °°) - (+ °°) = - °°. В аффинном режиме сравнения действуют по-другому, чем в проективном режиме: - 00< х < +°° для любого конечного числа х. 126
Оба режима правильно работают с цепными дробями. На рис. 4.5 показан пример, в котором аффинный режим работает, а проективный - нет. Если только сопротивление одного из резисторов равно нулю, оба режима дадут правильный ответ - нуль. Если же равны нулю сопротивления нескольких резисторов, проективный режим регистрирует особый случай недействительной операции, а аффинный режим все еще возвращает правильный ответ. О Рис. 4.5. Параллельное включение резисторов Программистам рекомендуется большей частью работать в проективном режиме и переходить на аффинный только в тех фрагментах программы, где нужно тщательно анализировать возможные источники и последствия бесконечностей как результатов операций. Опасность применения аффинного режима заключается в том, что знак бесконечности может оказаться неверным. Ниже мы покажем, как возникает эта опасность и почему применяется аффинный режим. Особый случай численного переполнения. Если особый случай численного переполнения замаскирован, результатом будет бесконечность со знаком истинного результата, вызвавшего переполнение. Например, деление с переполнением положительного числа на отрицательное дает-«>. Переполнение довольно опасно, так как знак может оказаться ненадежным и результат фактически не бесконечность, как при делении на нуль, а просто слишком велик для представления. Рассмотрим, например, деление большого числа на малое. Знак результата будет зависеть от знака малого числа, хотя он может быть "игрой" округления, т.е. будет ли малое число положительным или отрицательным, определяется ошибкой округления. Не рекомендуется маскировать особый случай переполнения, если программа тщательно не проанализирована и известно, что она допускает такие результаты. В этом случае целесообразно выбрать аффинный режим. Особый случай недействительной операции. Мы уже видели, что особый случай недействительной операции возникает, когда нет прием- 127
лемого результата, поэтому он является наиболее серьезным. Предположим, что особый случай недействительной операции замаскирован, и посмотрим, что происходит. Сначала рассмотрим, как влияют особые случаи недействительной операции на сравнения. Результатом выполнения команд сравнения процессора 80287 является код условия (аналогичный флажкам процессора 80286), который подробно описан в разд. 4.8. Обычно после сравнения двух чисел код условия показывает отношение меньше, равны или больше. Если в операции сравнения возникает особый случай недействительной операции (например, при сравнении бесконечности с конечным числом в проективном режиме), то код условия принимает специальное значение "не сравнимы". Следовательно, здесь обычный закон трихотомии арифметики (для любых двух чисел аиЬ всегда справедливо только одно из условий а> b,a = b,a<b) нарушается. Нарушение трихотомии вызывает некоторые трудности в языках высокого уровня. Рассмотрим оператор арифметического IF языка Фортран: IF (X) 10, 20,30 Он осуществляет переход к оператору 10, если X < 0, к оператору 20, если X = 0, и к оператору 30, если X > 0. Куда же переходить, если X "не сравним" с 0? Наверное, к процедуре обработки ошибки, но тогда компилятор должен образовать дополнительный условный переход. Даже более современный оператор if (х > 0) allright встречает аналогичные трудности. Оператор означает: если х > 0 или х = 0, то выполнить переход на метку allright (а затем перейти к следующему оператору), иначе просто выполнить следующий оператор. Большинство программистов ожидают, что если х "не сравним" с нулем, должен выполняться переход на метку allright. Однако многие компиляторы образуют для такого оператора примерно следующий код: сравнить х с 0 если меньше, перейти к 1 выполнить allright 1: Проблема заключается в том, что отрицанием отношения "больше или равно" оказываются "меньше или не сравнимы". Численные операции, отличающиеся от сравнений, возвращают численные результаты. Если особый случай недействительной операции возникает в такой операции, она возвращает как результат NAN (Not А Number - нечисло). Нечислом является любое значение с плавающей 128
точкой, поле порядка которого содержит 111... 11, а поле мантиссы - все, что угодно, кроме нулей; следовательно, различных нечисел очень много. С появлением этого определения исчерпано кодирование значений с плавающей точкой. Правила, по которым образуются и используются нечисла, очень просты. Если оба операнда операции являются нечислами, возникает особый случай недействительной операции, а результатом будет нечисло с большим абсолютным значением. Если возникает особый случай недействительной операции, но операндов нечисел нет, то результатом будет специальное нечисло, называемое неопределенностью. Неопределенность имеет в поле порядка число 111 ... 11, а в поле мантиссы - значение 1.1. Так как в форматах одинарной и двойной точности есть неявный старший бит мантиссы, неопределенность содержит в поле мантиссы число 1000 ... 00. Неопределенность в формате расширенной точности с явным старшим битом мантиссы имеет в поле мантиссы 11000... 00. Кроме неопределенности процессор 80287 не определяет ни одного нечисла. Все остальные коды нечисел программист использует по своему усмотрению. Так как процессор 80287 не разрешает нечислам превратиться в обычные числа с плавающей точкой, можно наблюдать, как недействительная операция влияет на последующие вычисления. Эффект распространения нечисел похож на инфекцию, и мы можем узнать, серьезна ситуация или нет. Однако пользуясь этим способом, не следует забывать, что недействительные операции влияют не только на значения, но и на условные переходы, предпринимаемые по этим значениям. Поэтому в программах, содержащих сравнения чисел с плавающей точкой, некоторые значения могут оказаться "зараженными", но не показывающими признаков "заболевания". 4.3. ФОРМАТЫ ЦЕЛЫХ ЧИСЕЛ Процессор 80287 имеет команды, которые преобразуют целые числа в числа с плавающей точкой, и наоборот. На рис. 4.6 показаны допустимые форматы целых чисел. Целое слово - это просто обычное 16-битное целое число процессора 80286. Короткое целое и длинное целое похожи на целое слово, но их длина больше. Упакованное десятичное число состоит из 18 десятичных цифр, размещенных по две в байте (см. гл. 3). Знаковый бит находится в дополнительном байте с левого (с наибольшим адресом) конца упакованного десятичного числа. Младшие семь бит этого байта должны быть нулевыми. 129 5 Заказ 6126
16 бит Рис. 4.6, Форматы целых чисел Целое число (Дополнительный! 2 байта 32 бита Короткое целое Дополнительный код 4байта 64 бита Длинное целое Дополнительный код 8 байт 1 бит 7 бит 72 бита Упакованное десятичное 1s ЮИ^т^бЫ • • • d1 d0 10 байт 4 бита Попытка преобразовать нечисло, денормализованное или ненормализованное числа, бесконечность или число, выходящее за диапазон, в целое число вызывает особый случай недействительной операции. А что происходит, если особый случай недействительной операции замаскирован? Процессор 80287 попытается запомнить неопределенность. Поскольку в форматах целых двоичных чисел нет наборов для представления специальных значений, преобразование неопределенности в любой из этих форматов дает 1000 ... 00, т.е. наибольшее отрицательное число. При преобразовании его в формат с плавающей точкой получается обычное отрицательное число, а не неопределенность. Преобразование неопределенности в упакованное десятичное число дает т.е. 8 байт "мусора" (в них могут быть любые значения) и два байта единиц. Попытка преобразовать такое число в формат с плавающей точкой приводит к неопределенному результату. Основное применение преобразований между целыми числами и числами с плавающей точкой связано с вычислениями, в которых имеются числа обоих форматов. Почему бы не реализовать целочисленную арифметику многократной точности путем преобразования в формат с плавающей точкой, производства вычислений в процессоре 80287, а затем преобразования в целые числа? Конечно, это возможно, но в большинстве случаев быстрее выполнить операции целочисленной арифметики в процессоре 80286, хотя при многократной точности на одну арифметическую операцию придется несколько команд. В табл. 4.2 показаны более быстрые варианты выполнения различных операций. Все целочисленные сложения и вычитания в процессоре 80286 выполняются быстрее, чем в 80287; так же обстоит дело с 16-битными умножениями и делениями. В остальных случаях быстродействие процессоров примерно одинаково. 1 1111111 1111 1111 хххх хххххххх... хххх, 130
Таблица 4.2. На более быстрый процессор для целочисленной арифметики Операция 16 бит. 32 бита 64 бита Сложение / вычитание 80286 80286 80286 Умножение / деление 80286 80286 80287 Приведенные в табл. 4.2 сведения могут изменяться в зависимости от особенностей программирования, быстродействия памяти и частоты синхронизации процессора. 4.4. РЕГИСТРЫ ПРОЦЕССОРА 80287 Процессор 80287 имеет регистры, удобные для вычислений над числами с плавающей точкой. Они показаны на рис. 4.7. Операнды команд процессора 80287 могут находиться в памяти или в одном из восьми численных регистров. Эти регистры хранят числа только в формате 80 бит R0 | R1 R2 ^_ R3 R4 R5 R6 R7 Численные регистры 16 бит Регистр управления Регистр состояния Регистр тэгов Рис. 4.7. Регистры процессора 80287 Указатель команды Указатель операнда 131
расширенной точности. Конечно, обращения к операндам в регистрах осуществляются намного быстрее, чем к операндам в памяти. Регистр управления содержит поля, значения в которых влияют на общее поведение процессора, например биты масок особых случаев. Содержимое этого регистра изменяется только при его явной загрузке программой. Регистр состояния имеет поля, показывающие внутренние условия процессора, но обычно не влияющие на его поведение, например биты кода условия, фиксирующие результаты сравнений. Регистр тэгов (признаков) применяется для регистрации особенностей содержимого численных регистров, чтобы повысить производительность и обнаружить особые случаи. Программисты используют его мало. Указатель команды и указатель операнда содержат адреса памяти последней команды процессора 80287 и ее операнда (если он есть). Эта информация предназначена для процедур обработки особых случаев. Остановимся на функциях регистров процессора подробнее. Численные регистры. Мы уже рассмотрели форматы численных регистров (числа с плавающей точкой расширенной точности), но адресация регистров довольно необычна. Номер численного регистра, указанного в команде, процессор всегда суммирует с содержимым поля ST (вершина стека) в регистре состояния. Сумма (берущаяся по модулю 8) определяет используемый численный регистр. Цель такой адресации заключается в том, чтобы превратить численные регистры в стек. На рис. 4.8 показана стековая интерпретация численных регистров в предположении, что поле ST содержит номер 5. Разработчики процессора знали, что большинство программистов захотят использовать численные регистры как стек, поэтому многие команды модифицируют вершину стека ST. Те же программисты, которые захотят использовать численные регистры как обычный набор регистров, а не как стек, должны позаботиться о неизменности вершины стека ST; в противном случае численные регистры подвергаются "перенумерации". 5 "if 132 Третий от вершины Четвертый от вершины ит. д. Вершина Первый от вершины Второй от вершины Численные регистры ST(3) ST(4) ST(5) ST(6) ST(7) ST(0) ST(1) ST(2) Рис. 4.8. Адресация численных регистров как стека
Регистр управления. Поля 16-битного регистра управления показаны на рис. 4.9. Справа находятся биты масок особых случаев: IM - маска недействительной операции, DM - маска денормализованного операнда, ZM - маска деления на нуль, ОМ - маска переполнения, , UM - маска антипереполнения, РМ - маска точности (неточного результата). 12 10-11 8-9 S 4 3 2 1 0 ■^^ю I рс I "рсТИНВ рм I umIomI zmI dmI im~1 Рис. 4.9. Регистр управления Когда бит маски содержит 0, возникновение соответствующего особого случая вызовет приостановку программы и появление прерывания процессора 80286 (подробнее см. гл.5). Если же бит маски установлен в 1, то соответствующий особый случай замаскирован и формируются специальные значения, как было показано в разд. 4.2. Назначение двухбитного поля управления точностью PC заключается в том, чтобы заставить процессор 80287 округлять все числа перед загрузкой их в численные регистры до указанной точности. Это поле может содержать следующие значения: 11 - округление до расширенной точности (принимается по умолчанию). 10 - округление до двойной точности, 00 - округление до одинарной точности. Кому же понадобится точность, меньшая максимальной? Одна из причин заключается в выполнении старых программ для других компьютеров. В этих программах могут быть скрыты приемы, зависящие от меньшей точности. Вторая причина - приспособление к тем языкам высокого уровня, которые требуют для промежуточных результатов конкретной точности. Например, язык Си требует, чтобы все промежуточные результаты имели двойную точность. Уменьшение точности с помощью поля PC не влияет на время выполнения команд. В регистре управления имеется двухбитное поле управления округлением RC. Мы уже говорили об имеющихся в процессоре 80287 четырех режимах округления. Они указываются в поле RC следующим образом: 00 - округление к ближайшему (принимается по умолчанию), 01 - округление к отрицательной бесконечности, 10 - округление к положительной бесконечности, 11 - округление к нулю. Последнее поле в регистре управления - бит управления бесконечностью 1С. Мы уже обсуждали различие между проективным и аффинным 133
режимами интерпретации бесконечности. Бит 1С задает режим следующим образом: 0 - проективный режим (принимается по умолчанию), 1 - аффинный режим. В слове управления микропроцессора 8087 есть дополнительное поле IEM (маска разрешения прерываний). Это поле управления прерываниями в процессоре 80287 не нужно благодаря более совершенному механизму прерываний в процессоре 80286 (см. гл. 5). Регистр состояния. На рис. 4.10 показаны поля регистра состояния. Справа находятся флажки особых случаев: IE - недействительная операция, DE - денормализованный операнд, ZE - деление на нуль, ОЕ - переполнение, UE - антипереполнение, РЕ - точность (неточный результат). 15 14 11-13 10 987 543210 В | Сэ | ST|Cg|C.|cn[hS Щ РЬ | Ub| ОЕ | ZE | РЕ| IE | Рис. 4.10. Регистр состояния При возникновении численного особого случая (замаскированного или нет) процессор устанавливает соответствующий флажок в состояние 1. Флажки особых случаев "зависают", т.е. процессор устанавливает их автоматически, но явно сбросить должен программист, загружая в регистр состояния новое значение. Например, программист может сбросить флажки особых случаев, замаскировать все особые случаи, а затем выполнить некоторые численные расчеты. После этого программа может проверить флажки особых случаев - не было ли в расчетах ошибок. Такой управляемый подход к особым случаям часто оказывается проще создания процедуры обработки особого случая, которая всякий рад вызывается при вычислениях. Бит суммарной ошибки ES устанавливается в 1, когда команда порождает незамаскированный особый случай. Биты С0, С19 С2, С3 содержат коды условий, являющиеся результатом сравнения или команды нахождения остатка. Интерпретация кода условия зависит от конкретной команды, поэтому подробнее о коде условия мы поговорим в разд. 4.8. Мы уже знакомы с трехбитным полем вершины стека ST; его содержимое прибавляется (по модулю 8) ко всем номерам численных регистров. Поэтому, если численные регистры используются как стек, поле ST 134
содержит номер регистра, являющегося верхним элементом стека (см. рис. 4.8). Бит занятости В устанавливается в 1, когда процессор выполняет команду или сигнализирует прерывание, а когда процессор свободен, этот бит сбрасывается в 0; его использование рассматривается в разд. 4.10. Регистр тэгов. Формат 16-битного регистра тэгов, состоящий из восьми двухбитных полей, показан на рис. 4.11. Каждое поле соответствует одному численному регистру и обозначает следующее: 00 - действительное число (т.е. любое конечное ненулевое число), 01 - нуль, 10 - недействительное число (например, нечисло или бесконечность), 11 - пустой регистр. Тэг 7 Тэг 6 Тэг 5 Тэг 4 ТэгЗ Тэг 2 Тэг 1 ТэгО Рис. 4.11. Регистр тэгов Регистры отмечаются как пустые, если они не инициализированы или их содержимое было "извлечено" из стека численных регистров. Процессор 80287 использует этот тэг для обнаружения антипереполнения стека (слишком много извлечений) и переполнения стека (слишком много включений). Обе ситуации приводят к возникновению особого случая недействительной операции. Когда этот особый случай замаскирован, но в операции возникает переполнение или антипереполнение стека, процессор корректирует ST, как будто ничего необычного не произошло, и возвращает как результат операции неопределенность. Указатели особого случая. Указатели особого случая (команды и операнда) предназначены для процедур обработки особых случаев. Они имеют два формата в зависимости от работы процессора 80287 в реальном или виртуальном режимах. Мы уже рассматривали реальный режим в гл. 2 и 3, а виртуальный режим обсуждается в гл. 5. Оба формата указателей особого случая показаны на рис. 4.12. В виртуальном режиме процессор дает селекторы и смещения подозрительной команды и ее операнда в памяти (если он есть). В реальном режиме указатели содержат 20-битные Адрес команды (0 — 15) Адрес команды I Код операции (О — 10) Адрес операнда (0-15) Смещение команды Селектор команды Смещение операнда Селектор операнда Реальный режим Виртуальный режим Рис. 4.12. Указатели особого случая 135
адреса этих объектов, а также 11 младших бит кода операции. Отметим, что эта дополнительная информация легко получается по содержимому указателя команды. Хотя указатели особого случая процессора 80287 в реальном режиме имеют такой же формат, как указатели особого случая микропроцессора 8087, между ними есть различие: указатель команды процессора 80287 показывает на первый префикс (если он есть) перед кодом операции, а указатель команды микропроцессора 8087 пропускает все префиксы. 4.5. форматы команд Форматы команд процессора 80287 аналогичны форматам команд 80286 (см. рис. 4.13 и приложение Б). Коды операций всех команд начинаются с пяти бит 11011, которые соответствуют коду операции ESC в процессоре 80286. Они предупреждают процессор 80286, чтобы он не пытался выполнять такую команду как свою. Ассемблерные мнемоники команд процессора 80287 начинаются с буквы F и их легко обнаружить в программе, так как таких мнемоник у процессора 80286 нет. Ассемблерная команда с обращением к памяти (см. рис. 4.13,о) FADD ANAME осуществляет прибавление числа с плавающей точкой из ячейки ANAME к содержимому численного регистра, на который показывает поле ST 5 бит 3 бита 2бита 3 бита 3 бита 1 0 1 1 | X X X mod XXX г*п | а) 1 1 0 1 1 X X X XXX ST(i) | б) 1 1 0 1 1 | X X X i XXX XXX в) Рис. 4.13. Форматы команд процессора 80287: a — команда с обращением к памяти, б — команда с обращением к численному регистру, в — команда без явных операндов 136
(верхний регистр стека). Допускаются все режимы адресации памяти процессора 80286, например FADD СВХЭ FADD ANAMEСВХ 3 CSI Э Напомним, что одна и та же мнемоника процессора 80286 применяется для сложения байт и слов ADD AL,A ADD АХ,А Ассемблер определяет генерируемую команду, обращаясь к типам переменной А и регистров AL, АХ. Это же относится и к командам процессора 80287. Программист может записать A DD 3.7 ; Число 3.7 с одинарной точностью В DQ 3.7 ; Число 3.7 с двойной точностью С DT 3.7 ; Число 3.7 с расширенной точностью Эти директивы распределяют память для константы 3.7 в соответствующем формате. Мнемоники DD, DQ, DT обозначают "определить двойное слово", "определить четверенное слово" и "определить десять байт". Тогда команда FADD А обращается к А как к числу с одинарной точностью, а команда FADD В полагает, что В является числом с двойной точностью. Отметим, что ассемблер допускает применять в определениях констант научную нотацию, например 1.23е5 равно 1.23 х 105. На рис. 4.13,6 показан формат команды, которая обращается к численному регистру, причем регистр / обозначается как ST(i'). Так как процессор прибавляет содержимое поля ST к номеру каждого регистра, правильная интерпретация ST(i) звучит как "/-й регистр от вершины регистрового стека". Например, команда FADD ST(3) прибавляет содержимое третьего регистра от вершины стека к содержимому верхнего регистра стека. На языке ассемблера верхний регистр 137
можно обозначить как ST(0) и ST (второе обозначение несколько туманнее). Например, команда FADD ST прибавляет содержимое верхнего регистра стека к нему же, т.е. удваивает содержимое регистра ST(0). На рис. 4ЛЗ,в показана команда без явной спецификации операндов. Например, команда FABS заменяет содержимое верхнего регистра стека на его абсолютное значение. Несмотря на отсутствие явных обращений, эта команда неявно обращается к верхнему регистру так же, как все команды FADD неявно обращались к этому же регистру. Далее мы увидим, что многий команды неявно обращаются к верхнему регистру стека. Перейдем к подробному рассмотрению всех команд процессора 30287. Информация о двоичных форматах и времени выполнения команд находится в приложении Б. 4.6. КОМАНДЫ ПЕРЕДАЧ ДАННЫХ Команды этой группы пересылают данные из памяти в численные регистры, из численных регистров в память, а также между численными регистрами. Имеются команды инициализации численных регистров на различные значения. Все команды передач данных приведены в табл. 4.3. Таблица 4.3. Команда передач данных Тип команды Мнемоника Операнд и действие команды Команды включения FLD Число с плавающей точкой в стек FILD Целое число FBLD Десятичное число Команды извлечения FSTP Число с плавающей точкой из стека FISTP Целое число FBSTP Десятичное число Команды копирования FST Число с плавающей точкой FIST Целое число 133
Окончание табл. 4.3 Тип команды Мнемоника Операнд и действие команды Команда обмена FXCH Обмен содержимого регистров Команды включения FLDZ Загрузить нуль в стек констант FLD1 Загрузить единицу FLDPI Загрузить л FLDLG2 Загрузить logi02 FIDLN2 Загрузить 1о^2 FLDL2T Загрузить log2 10 FLDL2E Загрузить log2 е Команды загрузки. Команда FLD имеет один операнд, которым может быть численный регистр или число с плавающей точкой (любого формата) в памяти. Операнд включается в вершину регистрового стека. Действие команды FLD показано на рис. 4.14,а. Сначала производится считывание операнда и преобразование его в формат числа с расширенной точностью (если он уже не представлен в таком формате), затем осуществляется декремент содержимого ST на 1 (по модулю 8) и, наконец, операнд помещается в новый верхний регистр. Например, команда A DD ? ; Переменная с одинарной точностью FLD А преобразует переменную А в формат с расширенной точностью и включает ее в регистровый стек. Команда FLD STC3) включает в регистровый стек копию содержимого регистра ST(3). После ее выполнения старый регистр ST(3) будет называться ST(4), поэтому содержимое ST(0) будет равно содержимому регистра ST(4). Команды FILD и FBLD аналогичны команде FLD, но их операндами являются целое и десятичное числа в памяти. В команде FILD допускаются все форматы целых чисел (16, 32 и 64 бита). До включения в регистровый стек операнд преобразуется в число с плавающей точкой расширенной точности. Следовательно, команды FILD и FBLD осуществляют преобразования форматов. Мнемоники команд с целочисленным (двоичным) операндом начинаются с "FI", а с десятичным операндом - с "FB". 139
ST- new value ST- ST(0) ST(1) ST(0) ST(1) ST(2) a) ST- 6) ST<0) ST(1) ST- ST(2) ST(0) ST(1) ST- e) ST(0) ST- ST(1) ST(0) ST(1) Рис. 4.14. Действия команд передач данных: а — действие на регистровый стек команды загрузки, б — действие на регистровый стек команды запоминания и извлечения, в — действие на регистровый стек команды запоминания Команды запоминания и извлечения из стека. Команда FSTP извлекает содержимое верхнего регистра стека и передает его в указанный получатель. Действие ее показано на рис. 4.14,6. Единственным операндом команды FSTP может быть численный регистр или переменная с плавающей точкой (в любом формате) в памяти. Например, команда 140
DD ? ; Переменная с одинарной точностью FSTP А преобразует содержимое верхнего регистра стека в число А с одинарной точностью, а затем извлекает содержимое верхнего регистра из стека. Подробнее действия команды FSTP выглядят так: считывается содержимое регистра, указываемого полем ST; выполняются необходимые преобразования; результат запоминается в операнде команды FSTP и производится инкремент содержимого регистра ST на 1 (по модулю 8). Следовательно, после выполнения команды FSTP ST(3) регистр ST(3) превращается в ST(2) и будет иметь содержимое старого регистра ST(0). Команда FSTP ST(0) извлекает вершину стека и "выбрасывает" ее содержимое. Команды FISTP и FBSTP аналогичны команде FSTP, но их операндами являются целое (двоичное) число в памяти (в любом формате) и упакованное десятичное число э памяти. Следовательно, команда FISTP производит преобразование числа с плавающей точкой в целое, а команда FBSTP - в упакованное десятичное. В процессе преобразования необходимо округлять число с плавающей точкой до целого. Команда FISTP выполняет округление в соответствии с полем управления округлением RC. Команда FBSTP игнорирует регистр управления и осуществляет округление путем прибавления числа 0,5 и отбрасывания дробной части результата. Такое несоответствие режиму округления имеется только в команде FBSTP. Команды запоминания. Команда FST копирует содержимое регистра ST(0) в численный регистр или память (операнд с одинарной или двойной точностью), производя преобразование из формата с расширенной точностью. Указатель стека ST не модифицируется. Команда FST не может запоминать источник в операнде с расширенной точностью. Действие (т.е. его отсутствие) команды FST на регистровый стек представлено на рис. 4.14,в. Например, команда FST ST(3) копирует содержимое регистра ST(0) в регистр ST(3). 141
Команда FIST аналогична команде FST, но ее целочисленный операнд в памяти имеет длину 16 или 32 бита. Команды FBST в процессоре нет, потому что его разработчикам из-за нехватки наборов для кодов операций приходилось чем-то жертвовать. Однако отметим, что запоминание можно смоделировать командами загрузки (включения в стек) и запоминания с извлечением из стека. Например, команды FLD ST(0) ; Продублировать ST(0) FBSTP DECVAR ; Преобразовать в десятинное и извлечь действуют так же, как отсутствующая команда "FBST DECVAR". Команда обмена регистров. Команда FXCH имеет один операнд, являющийся численным регистром, и обменивает его содержимое с содержимым регистра ST(0). Указатель стека ST не модифицируется. Например, команда FXCH ST<3) обменивает содержимое регистров ST(0) и ST(3). Команда FXCH выполняется примерно вдвое быстрее команд FLD, FSTP и FST. Команды загрузки констант. Эти команды предназначены для включения в стек часто используемых констант. Например, команда FLD1 включает в регистровый стек константу 1.0. Остальные команды загрузки констант приведены в табл. 4.3. Они выполняются примерно вдвое быстрее загрузки констант из памяти. 4.7. АРИФМЕТИЧЕСКИЕ КОМАНДЫ Базовые арифметические команды сложения, вычитания, умножения и деления имеют два операнда (источник и получатель) и реализуют следующее действие: приемник «- приемник ор источник, где ор = +, -, *, /. Для некоммутативных операций вычитания и деления имеются обратные варианты команд: приемник«- источник ор приемник, где ор = -, /. Во всех случаях один из операндов должен быть в регистре ST(0). Имеются шесть форм базовых команд: a) Fxxx flf)Fxxx память 142
\ eJFIxxx память aJFxxx ST,ST(i) d^Fxxx ST(4ST e)FxxxP ST(/>>ST где xxx - ADD, SUB, MUL, DIV, SUBR (обратное вычитание) и DIVR (обратное деление). Действия команд всех шести форм (для вычитания) показаны на рис. 4.15. В форме а процессор извлекает из стека верхний операнд (источник) и следующий операнд (приемник), а затем включает в стек результат операции. В форме б адресуемый операнд в памяти является источником, а регистр ST(0) - приемником. Указатель стека не модифицируется. FSUB ST—w а) ST—» б) и в) ST—* ST- д) ST- Рис. 4.15. Действия различных команд вычитания е) ST- ST—# х- mem ST(0) ST- ST(I) ST(0) ST- ST(i) ST(0) ST(1) ST- ST(i) х-У JLJL FSUB mem или RSUBmem FSUB ST. ST (i) ST(0) ST(i) ST(0) ST(i) FSUB ST (I). ST FSUBPST(i). ST ST(0) ST(M) 143
Допускаются только операнды с одинарной и двойной точностью. Форма в аналогична форме б, но операнд в памяти является 16> или 32-битным целым числом, а не числом с плавающей точкой. Преобразование в формат с плавающей точкой производится автоматически. В форме г любой численный регистр ST(i) служит источником, a ST(0) - приемником. Указатель стека ST не изменяется. В форме д ST(0) является источником, a ST(/) - приемником и указатель стека не модифицируется. Наконец, в форме е численный регистр ST(i) является получателем, а ST(0) - источником. По окончании операции источник ST(0) извлекается из стека. В табл. 4.4 показаны 18 ассемблерных мнемоник, полученных из форм а-е для шести базовых арифметических операций. Таблица 4.4. Базовые арифметические команды Операция Стек Целое Извлечение Сложение FADD FIADD FADDP Вычитание FSUB FISUB FSUBP Обратное вычитание FSUBR FISUBR FSUBRP Умножение FMUL FIMUL FMULP Деление FDIV FIDIV FDIVP Обратное деление FDIVR FIDIVR FDIVRP Теоретически одних команд передач данных и команд в форме а достаточно для всех применений. Например, можно смоделировать форму б команды FADD DOUBL ; Прибавить DOUBL к ST(C^ двумя командами FLD DOUBL ; Включить DOUBL в стек FADD Однако одна команда в форме б быстрее двух команд примерно на 16 % (если переменная DOUBL имеет формат с двойной точностью). Форма а является частным случаем формы е, введенным для удобства программистов. Запись команды FADD приводит к такой же машинной команде, как и запись FADDP ST(1),ST 144
Мы рассмотрели наиболее часто используемые команды процессора 80287. До перехода к остальным командам дадим несколько примеров программирования. Программирование со стеком. Работа со стековыми командами не вызывает трудностей, особенно у людей, знакомых с работой на стековых калькуляторах. Вот как, например, вычисляется выражение Ъ2 - 4ос: ; Включить b в стек ; b ч2 заменяет b в вершине ; Полагаем,что FOUR содержит 4.0 ; 4а заменяет 4 ; 4ас заменяет 4а ; Вычесть ST(0) из ST(1) Регистровые переменные. Предположим, что требуется вычислить статистические оценки для двух наборов данных (х19 х2,..., хп) и (у1} у2, ..., уп) по следующим соотношениям: М* = | *1 > МУ = | У( > s* = i A' SY = i • °ху -1 *1 У г • Конечно, можно накапливать эти пять сумм в памяти, но гораздо быстрее использовать для этой цели регистры. Язык Си позволяет программистам указывать на то, что некоторые переменные используются интенсивно и их следует хранить в быстродоступных регистрах, а не в памяти. Такие переменные называются регистровыми. В других языках сложные компиляторы могут определить, какие переменные должны быть регистровыми без явных указаний программиста. Как же использовать численные регистры процессора 80287 в качестве регистровых переменных? Это трудно осуществить в системе команд со стековой ориентацией, но можно воспользоваться только теми формами команд, которые сохраняют указатель стека ST неизменным. Если сделать так, то для каждого i обозначение ST(/) будет относиться к одному и тому же регистру. У нас появляются команды следующих видов: ST(i) < ST<i) op ST(j) <i«0 или j«0) ST<i) < ST(j) <i-0 или j«0) ST<0> <—> ST(i) (обмен) ST<0) < память ST(0) > память ST(0) < ST(0) od память где op обозначает шесть базовых арифметических операций. К сожалению, нет команды загрузки регистра ST(0) без включения в стек (т.е. без декремента указателя стека ST) и мы не можем реализовать передачи ST(0) <— ST (i) ST(0) <— память FLD b FMUL ST,ST(0) FLD FOUR FMUL a FMUL с FSUB 145
одной командой. Но эту трудность легко преодолеть, поместив перед каждой командой FLD команду FSTP ST. Эта команда производит извлечение из стека, "выбрасывая" содержимое верхнего регистра ST(0). Поэтому для загрузки ST(0) <— ST(5) привлекаются две команды: FSTP ST ; Извлечь вершину стека FLD ST(4) $ Включить ST<4) в стек В любом случае для передач между регистрами вместо команд FLD или FST следует попытаться воспользоваться командой обмена FXCH, так как она выполняется быстрее. Попробуем запрограммировать внутренний цикл для вычисления приведенных выше сумм без инициализаций и проверки окончания цикла. Распределим регистры следующим образом: SI хранит индекс цикла, ST(1) хранит Мх, ST(2) хранит My, ST(3) хранит Sx, ST(4) хранит Sy, ST(5) хранит Cxy. Вот как выглядит программа: FSTP ST FLD XCSIЭ ; ST(0) < — X (i ) FADD ST<1>,ST ; Mx <- - Mx + ST(0) FST ST<6) ; ST (6) < — ST(0) FMUL ST<0>,ST ; ST(0) < — ST<0) * ST(0) FADD ST(3),ST ; Sx <- - Sx * ST(0) FSTP ST FLD YCSI3 ; ST(0) < — Y(i) FADD ST(2),ST ; My <- - My + ST(0) FST ST (7) ; ST<7) < — ST(0) FMUL ST<0>,ST ; ST(0) < — ST(0) ♦ ST(0) FADD ST(4),ST ? Sy <- " Sy + ST<0) FXCH ST<6) ; ST(0) < — ST(6) FMUL ST,ST(7) ; ST(0) < — ST<0) ♦ ST (7) FADD ST<5),ST ; Cxy < — Cxy + ST<0) Общие подвыражения. Арифметическое выражение иногда содержит подвыражение, фигурирующее в нескольких местах, т.е. дублируемое. 146
Такое подвыражение называется общим. Например, в операции деления комплексных чисел а + ib _ ac + bd . cb-ad _ c + id = c2+d2 +t c2+<f ~e + %1 имеется общее подвыражение с2 + d2. При вычислении всего выражения целесообразно сохранить значение общего подвыражения в так называемой временной переменной, чтобы не тратить время на вычисление общего подвыражения несколько раз. Временную переменную после окончания вычислений можно удалить или использовать повторно. Например, можно вычислить два числа ей/, выполнив шесть (а не восемь) умножений, три (а не четыре) сложения / вычитания и два деления. Если разделить числители и знаменатели на с, получим выражение a + ib= а + b(d/c) Ъ - a(d/c) _ f c + id c-hd(d/c) 1 c + d(d/c)~e %U которое содержит два общих подвыражения d/cnc + d(d/c). Следовательно, можно вычислить ей/, выполнив всего три умножения, три сложения / вычитания и три деления по следующей схеме: tt - d/c t2~c + dtx e~(a + btx)/t2 f-(b-atxyt2 Конечно, ради достижения высокого быстродействия временные переменные следует хранить в регистрах, а не в памяти. Можно ввести временные регистровые переменные для общих подвыражений, пользуясь тем же приемом "неизменного ST", который применялся ранее для регистровых переменных. Получается следующая программа деления комплексных чисел: FSTP ST FLD D FDIV С FST ST(1) FMUL D FADD С FXCH ST(2) FSTP ST FLD ST<0> FMUL В FADD A FDIV ST,ST(2) ; Загрузить d ; Образовать tl ■ d/c в ST(1) ; Образовать t2 » с + dtl в ST<2) ; Загрузить tl
FST E ; Образовать (a + btl)/t2 в E FXCH ST(1) ; Передать tl ■ ST(B) FMUL A FSUBR В FDIV ST,ST(2) FST F ; Образовать (b - atl)/t2 в F Этот фрагмент программы можно реализовать еще быстрее; мы оставляем эту задачу читателям. Дополнительные арифметические команды. Кроме рассмотренных базовых операций процессор 80287 имеет семь дополнительных арифметических команд, которые приведены в табл. 4.5. Таблица 4.5. Дополнительные арифметические команды Мнемоника команды Описание команды FSQRT FSCALE FPREM FRNDINT FXTPACT FABS FCHS Извлечение квадратного корня Масштабирование на степень числа 2 Нахождение частичного остатка Округление до целого Выделение порядка и мантиссы Нахождение абсолютной величины Изменение знака Команда FABS (без явных операндов) заменяет содержимое регистра ST(0) на его абсолютное значение, а команда FCHS изменяет (т.е. инвертирует) знак содержимого регистра ST(0). Команда FRNDINT округляет содержимое регистра ST(0) до целого числа в формате с плавающей точкой, а команда FSQRT заменяет содержимое ST(0) на значение корня квадратного из него. Последняя операция выполняется очень быстро, даже быстрее деления. Она удобна для тех применений, где требуется часто вычислять расстояние между точками в пространстве: расстояние (p,q) = sgrt [(p^-g*)2 + (py-gy)2 + (pz-gz)2l а также при вычислении элементарных трансцендентных функций. Команда масштабирования FSCALE прибавляет значение (считающееся целым числом в формате числа с плавающей точкой) из регистра ST(1) к порядку числа в регистре ST(0), заменяя старое значение в ST(0), т.е. быстро умножает или делит число с плавающей точкой на степень числа 2. Для масштабирования последовательности значений на один и тот же масштабный коэффициент нужно только загрузить, масштабировать и запомнить с извлечением каждый элемент последовательности без промежуточных стековых операций. 148
Команда EXTRACT разлагает содержимое регистра ST(0) на два числа с плавающей точкой: несмещенный порядок и знаковую мантиссу. Порядок заменяет старое значение ST(0), а мантисса включается сверху. Отметим, что команда FSCALE, находящаяся после команды EXTRACT, восстанавливает исходное число. Команда EXTRACT удобна в тех случаях, когда порядок и мантисса должны обрабатываться отдельно, например при преобразовании чисел с плавающей точкой в символьный код ASCII. Команда нахождения частичного остатка FPREM вычисляет остаток от деления содержимого ST(0) на число из ST(1). Знак остатка совпадает со знаком содержимого ST(0). Остаток заменяет содержимое ST(0). Команда FPREM формирует частичный остаток, так как она может прекратиться, не завершив операции деления. Она работает, производя повторяющиеся масштабированные вычитания, и за 64 вычитания может уменьшить величину разности на 264. Если 64 или меньшего числа вычитаний достаточно для того, чтобы сделать число в ST(0) меньше, чем число в ST(1), то после выполнения команды FPREM в регистре ST(0) будет находиться правильный остаток, а бит С2 кода условия сбрасывается в 0. Если 64 вычитаний недостаточно, то бит С2 устанавливается в 1, а в регистре ST(0) находится уменьшенный результат, полученный после 64-го вычитания. Повторное выполнение команды FPREM до образования С2 = 0 дает точный остаток. Команда FPREM осуществляет только 64 вычитания для того, чтобы между повторными ее выполнениями можно было прервать процессор 80287. Выбор 64 гарантирует, что команда FPREM не будет длиться дольше команды деления, т.е. не превысит максимальной задержки прерывания. Предполагается, что команда FPREM формирует в битах С0С3С1 кода условия три младших бита частного. К сожалению, эта возможность реализована в процессоре 80287 не полностью и биты кода условия не надежны в последовательных выполнениях команды FPREM. Однако эти биты не влияют на вычисляемый частичный остаток. Дополнительная информация в коде условия предназначена помочь в вычислении тригонометрических функций. Собственно основное применение команды FPREM и связано с этими функциями. Далее мы приведем примеры использования команды FPREM. Целесообразно проанализировать информацию в табл. 4.6, показывающую время выполнения арифметических команд. Предполагается, что частота синхронизации составляет 5,3 МГц, а при ее увеличении до 8 МГц быстродействие повышается примерно на 50 %. Подробная информация о 149
командах содержится в приложении Б. Пользуясь табл. 4.6, можно определить, достаточно ли быстродействия процессора 80287 в конкретном применении или потребуется более мощный компьютер. Таблица 4.6. Производительность процессора 80287 (частота синхронизации 5,3 МГц) Операция Время, мкс Скорость, Коп. с СЛОЖЕНИЕ регистр-регистр 16 63 УМНОЖЕНИЕ регистр-регистр 26 38 ДЕЛЕНИЕ регистр-регистр 37 27 ИЗВЛЕЧЕНИЕ КОРНЯ КВАДРАТНОГО 35 29 ЗАГРУЗКА двойная точность 9 111 ЗАПОМИНАНИЕ двойная точность 19 53 ЗАГРУЗКА 32-битное целое 11 91 ЗАПОМИНАНИЕ 32-битное целое 17 59 4.8. КОМАНДЫ СРАВНЕНИЙ Команды сравнений процессора 80287 показаны в табл. 4.7. Команда FCOM х сравнивает содержимое ST(0) с операндом х и устанавливает коды условия в соответствии с табл. 4.8. Операндом х может быть содержимое численного регистра или число в памяти (с одинарной или двойной точностью). Указатель стека ST не модифицируется. Программист может написать команду FCOM как сокращенный вариант команды FCOM ST <1) Команда ПСОМ действует аналогично команде FCOM, но операндом ее является 16- или 32-битное целое число в памяти. Команда FCOMP аналогична команде FCOM, но после сравнения она производит (один раз) извлечение из стека. Такие же действия осуществляет команда FICOMP. Наконец, команда FCOMPP (без явных операндов) действует как команда FCOM, но после выполнения сравнения она извлекает из стека оба операнда. Команда проверки FTST сравнивает содержимое ST(0) с нулем и устанавливает коды условия в соответствии с табл. 4.9. Команда анализа FXAM аналогична команде FTST, но она получает о содержимом регистра 150
Таблица 4.7. Команды сравнений Мнемоника команды Описание команды FCOM Сравнение FICOM Целочисленное сравнение FCOMP Сравнение с извлечением из стека FICOMP Целочисленное сравнение с извлечением из стека FCOMPP Сравнение и двойное извлечение из стека FTST Сравнение с нулем FXAM Анализ Таблица 4.8. Коды условия после сравнения С3 Со Условие 0 0 ST(0) > х 0 1 ST(0) < х 1 0 ST(0)*x 1 1 ST(0) и x не сравнимы Таблица 4.9. Коды условия после проверки Сз С0 Условие 0 0 ST(0) > 0 0 1 ST(0) < 0 1 0 ST(0) = 0 1 1 ST(0) и 0 не сравнимы ST(0) гораздо больше информации, чем команда FTST. Установка кодов условий после команды FX AM показана на рис. 4.16. Как это ни парадоксально, но команда FXAM выполняется примерно вдвое быстрее команды FTST. Выполнение команды FXAM служит быстрым способом инициализации кодов условия на любое требуемое значение. Мы не рассмотрели еще вопрос о том, как использовать коды условий процессора 80287 для условных переходов. Реализация их несколько неудобна, так как процессоры 80286 (осуществляющий переход) и 80287 (содержащий коды условий) - это разные микросхемы. Хуже того, поскольку микропроцессор 8086 (совместимый предшественник 80286) уже производился до разработки 8087 (совместимый предшественник 80287), микропроцессор 8086 изменить в помощь микропроцессору 8087 было нельзя. 151
С1 = 0 Cj о 1 Отрицательное тельное г Cq-0 Конечное Не конечное С2 U 0 0 Нечисло о Бесконеч1 ность 0 Пустой 1 1 Пустой Рис. 4.16. Коды условий, формируемые командой анализа Ассемблерная команда процессора 80287 FSTSW АХ копирует содержимое его регистра состояния (фиксирующий коды условий) в регистр АХ процессора 80286. Затем коды условий проверяются командами этого процессора. Для этого применяется интересный прием. В процессоре 80286 есть команда SAHF (запомнить содержимое регистра АН во флажках), которая была введена в микропроцессор 8086 ради совместимости с микропроцессором 8080. Бели сравнить два числа с плавающей точкой с помощью команды FCOM, затем передать содержимое регистра состояния процессора 80287 в регистр АХ командой FSTSW, а содержимое регистра АН во флажки с помощью команды SAHF, то получится результат, показанный на рис. 4.17. В частности, бит С3 попадает во флажок нуля ZF, а бит С0 - во флажок переноса CF. Теперь можно убедиться, что результат команды FCOM по установке флажков такой же, □ □ □ 152 Так регистр АН отображается на регистр флажков с помощью команды SAHF Так регистр состояния запоминается с помощью команды FSTSW АХ Рис. 4.17. Соответствие кодов условий процессора 80287 флажкам процессора 80286
как и команды СМР процессора 80286 за исключением, конечно, условия "не сравнимы", которого не может быть в процессоре 80286. Например, команды FCOM FSTSW АХ SAHF JE noaccident осуществляют переход к метке noaccident, если ST(0) = ST(1). Размещение бит кода условия в регистре состояния и значения кода условия от команд FCOM и FTST выбраны так, чтобы правильно работал показанный фрагмент. 4.9. ТРАНСЦЕНДЕНТНЫЕ КОМАНДЫ К элементарным трансцендентным функциям относятся тригонометрические функции (sin, cos, tg и др.), обратные тригонометрические функции (arcsin, arccos, arctg и др.), логарифмические функции (log2, log10, log), показательные функции (ху, 2х, 10*, 6х), гиперболические функции^, ch, th и др.) и обратные гиперболические функции (arsh, arch, arcth и др.). Процессор 80287 может быстро вычислить любую из элементарных трансцендентных функций с аргументами двойной точности, давая результат двойной точности с ошибкой в единицу младшего разряда. Более того, вычисляемые тригонометрические функции удовлетворяют всем тригонометрическим тождествам с ошибкой округления. Основой реализации таких возможностей служат формат чисел с расширенной точностью, быстрая команда извлечения корня квадратного, команда нахождения точного частичного остатка и трансцендентные команды, показанные в табл. 4.10. Разработчики процессора 80287 следовали тому принципу, что аппаратно нужно реализовать только наиболее длительные "внутренние циклы" вычислений. Поэтому каждая элементарная функция вычисляется небольшой подпрограммой. Этим достигается максимальная производительность при ограниченных аппаратных ресурсах. Для иллюстрации практического применения такого подхода мы рассмотрим вычисление функции тангенса. Команда FPTAN нахождения частичного тангенса в качестве результата дает два таких числа х и у, что у/х = tg(ST(0)). Число у заменяет старое содержимое ST(0), а число х включается сверху. Аргумент команды FPTAN должен быть нормализован; денормализованные и ненормализованные числа, бесконечности и нечисла считаются недопустимыми. Такое же ограничение действует и в остальных трансцендентных коман- 153
Таблица 4.10. Т^ансцеидешыыекоманды Мнемоника команды Описание команды FPTAN FPATAN FYL2X FYL2XP1 F2XM1 Частичный тангенс Частичный арктангенс Вычисление у log2(x) Вычисление у loga(x +1) Вычисление 2х -1 дах. Кроме того, аргумент команды FPTAN должен находиться в диапазоне 0 < ST(0) < л/4. Если аргумент оказывается недопустимым или находится вне диапазона, команда. FPTAN дает неправильный результат, не сигнализируя об особом случае. Так же ведут себя и остальные трансцендентные команды, у каждой из которых есть свой диапазон применимости. Поэтому программист должен сам позаботиться о допустимости аргумента и приведении его в требуемый диапазон. Допустимость аргумента j егко проверить с помощью команды FXAM, поэтому мы остановимся на приведении аргумента. Обозначим аргумент через z. Бели z отрицательный, его тангенс можно вычислить с привлечением тождества tg(z) = -tg(-z), поэтому можно считать, что z > 0. Если применить команду нахождения остатка к гил, получится такой остаток г, что где q - частное. Благодаря тождеству tg(nn + х) = tg(x) для всех целых п, мы имеем tg(z) - tg(r) и теперь нужно вычислять тангенс угла между 0 и л. Если г > л/2, можно применить тождество tg(x) = -l/tg(x - л/2). В результате задача свелась к вычислению тангенса угла между 0 и л/2. Если рно превышает л/4, можно применить тождество tg(x) = l/tg(rc/2 - х) и потребуется вычислять тангенс числа между 0 и л/4. Следовательно, аргумент находится в диапазоне команды FPTAN. Вот как выглядит программа вычисления tg(z) без проверки допустимости г. z = qn + г, 0 < г < л, FLDPI I Константа ПИ FLD z I Аргумент тангенса FTST $ Проверить отрицательный аргумент ; Инициализировать флажок инверсии I Инициализировать флажок "изменения знака ; Передать состояние в АХ ; ZF » СЗ, CF - С0 MOV ВХ,0 СХ,ВХ MOV FSTSW АХ SAHF 154
JAE remainder FCHS NOT CX remainder: FPREM FSTSW AX SAHF JP remai nder Перейти,если z положительный tg<z) = -tg(-z) Устновить флажок изменения знака Вычислить z по модулю ПИ Передать состояние в АХ PF = С2 Повторять до завершения FLD FSTP FCOM FSTSW SAHF JBE FSUB NOT NOT piover^ ST (2) 1epi over2 STU) BX CX Константа ПИ/2 В вершине стека г и ПИ/2 Сравнить г с ПИ/2 Передать состояние в АХ ZF = СЗ, CF = СИ Перейти, если г <= ПИ/2 tg<r> - -l/tg<r - ПИ/2) Установить флажок инверсии Дополнить флажок изменения знака lepiover2: FLD FCOMP FSTSW SAHF JAE FSUBR NOT 1epiover4: FPTAN AND JZ FCHS piover4 AX 1epi over4 ST,ST(1) BX CX ,CX nonegate ; Константа ПИ/4 ; Сравнить ПИ/4 с г и извлечь ; Передать состояние в АХ ; ZF = СЗ, CF - СИ ; Перейти, ее ли ПИ/4 >=* г ; tg(r) = l/tg<nn/2 - r> Дополнить флажок инверсии ST<1)/ST<fl) тангенс Проверить флажок изменения знака Перейти,еели не установлен ; STU) / <-ST<H) ) = -тангенс nonegate: AND ВХ,ВХ JZ noinvert FDIVR JMP DONE noinverti FDIV donei ; Проверить флажок инверсии j Перейти,если не установлен ; 1/тангенс - ST(0)/ST(1) ; Закончено ; ; Тангенс ■ STU>/ST<0> Приведенная программа (она не самая быстрая) длится примерно 255 мке в предположении, что команда FPREM выполняется только один раз, а частота синхронизации составляет 5,3 МГц. Это соответствует вычисле- 155
нию около 3900 значений тангенсов в секунду. Время на выполнение команд процессора 80286 пренебрежимо мало. Значения остальных тригонометрических функций можно вычислить аналогично, пользуясь командой FPTAN, формулами из табл. 4.11 и командой FPREM для приведения аргумента. Формулы в табл. 4.11 выражены через tg(z/2), а не через tg(z). Деление на 2 быстро осуществляется командой FSCALE, а применение показанных формул значительно уменьшает ошибку округления. Таблица 4.11. Формулы для тригонометрических функций sinfr)- 2(У/Х) 2> ctg(z/2)=*/y, 1 + (у/х) 1 + (у/х)2 2Ш \ + {у/х? tg(2/2) = y/x, «Ю- ——2 1 - (у/х) z — значение в вершине стека до выполнения команды fptan , х, у — значения в регистрах st (0) и st (1) после деления на 2 и выполнения команды fptan. Команда FPATAN вычисляет arctg(ST(l)/ST(0)). Два верхних элемента извлекаются из стека, а результат включается в стек. Операнды этой команды должны удовлетворять условию 0 < ST(1) < ST(0). Они легко приводятся в этот диапазон с помощью тождеств: arctg(x) = -arctg(-x), arctg(x) = л/2 - arctg(l/x). Остальные обратные тригонометрические функции находятся с помощью команд FPATAN, FSQRT и формул, приведенных в табл. 4.12. Команда FYL2X вычисляет функцию ST(l)xllog2ST(0). Два операнда извлекаются из стека, а затем результат включается в стек. В команде требуется удовлетворения естественного для логарифмической функции условия ST(0) > 0. Значения других логарифмических функций вычисляются с привлечением команды FYL2X, формул из табл. 4.13 и команд загрузки констант FLDLN2 и FLDLG2. 156
Таблица 4.12. Формулы для обратных тригонометрических функций arcinfe)-.rctg( V(1_;(1+z))° arctg(y/x) arcctg(z) = arctg(l/jc) = arctg(y/x) "(г)°"Ч/(Д^1) » arctg(y/x) arcsec(z) = 2arctj « 2arctg(y/x) 2 — аргумент требуемой обратной тригонометрической функции f, X, у — значения в регистрах ST(0) и ST(1) перед выполнением команды FPATAN, так что результат в вершине стека равен f(z). log2W=FYL2X(x) logeW = loge(2) log2(x) = FYL2X (loge(2),x) = FYL2X (FLDLN2,*) log, (x) = log1() (2) log2 (x) - FYL2X (log*10 (2),x) = FYL2X (FLDLG2,*) Еще одна логарифмическая команда FYL2XP1 вычисляет функцию ST(l)x log2 [ST(0) -г1]. Здесь значение ST(0) должно удовлетворять более жесткому ограничению |ST(0)| < 1 - l/JT. Причина появления этой команды заключается в получении более высокой точности вычисления функции log(l + х). Эта функция встречается в финансовых расчетах, а также при вычислении обратных гиперболических функций (см. формулы в табл. 4.14). Кажется, что функцию log(l + х) можно вычислить, прибавив 1 к х, а затем воспользовавшись командой FYL2X. Однако при малом х прибавление 1 вызовет потерю ценных бит мантиссы х и приведет к неточному результату. Если аргумент слишком велик для команды FYL2XP1, то прибавление 1 не вызовет потери значимости и использование команды FYL2X даст приемлемо точный результат. Команда показательной функции F2XM1 вычисляет 2ST^ - 1, причем содержимое ST(0) должно находиться в диапазоне 0 < ST(0) ^ 0,5. Таблица 4.13. Формулы для логарифмических функций 157
Таблица 4.14. Формулы для обратных гиперболических функций arsh(x) = [ sign(x)] [loge(2)] [log,, (1 +z)] = FYL2XP1 (sign (x) (FLDLN2, z\ где*Чх1+ lx) ■ u arch (x) = [ loge (2)][log2 (1+z)] = FYL2XP1 (FLDLN2, z), где *«x-l + V(x-l)(x+l) и x> 1 arcth (x) = [ sign (x) ] [ loge (2) ] [ log2 (1 + z)] = FYL2XP1 (FLDLN2 sign (x), z), arcth (x) - arcth (1/x) arcsh (x) ■ arsh (1/x) arsch (x) = arch (1/x) Вычисление функции 2х - 1 вместо функции 2х позволяет избежать потери точности, когда аргумент х близок к 0 (а значение функции 2х близко к 1). Эта же команда, как показано в табл. 4.15, удобна при вычислении гиперболических функций. Если же потребуется функция 2* она вычисляется путем прибавления 1 к результату, полученному командой F2XM1. Остальные показательные функции можно получить с помощью команды F2XM1, пользуясь формулами иэ табл. 4.16. Таблица 4.15. Формулы для гиперболических функций ch(x) 2L Jx\\ th (x) = sign (x) - cth(x) = l/th(x) csh(x) = l/sh(x) sch(x) = l/ch(x) 158
Таблица 4.16. Формулы дня показательных функций 2х- (2х- 1) + 1-F2XM1 (х) +1 ех = 1 + (2* l0g2 (e) - 1) = 1 + F2XM1 (х Хоц (е)) = 1 + F2XM1 (xFLDL 2Е) 10* = 1 + (2* l0g2(10) - 1) = 1 + F2XM1 (х lo^ (10)) = 1 + F2XM1 (xFLDL 21) хУ = 1 + (2^^ l0g2 W - 1) = 1 + F2XM1 (у log2 (х)) - 1 + F2XM1 (FYL2X (у, х) Ограничение диапазона аргумента команды F2XM1 снимается при вычислении функции 2* - 1 для произвольного х следующими действиями: Шаг 1. Вычислить / = FRNDINT (х) и/ = х - и Этим гарантируется, что l/i < 1/2, так как в режиме округления по умолчанию команда FRNDINT округляет к ближайшему целому. Следовательно, получается 2 * = =(2')(2'). Шаг 2. Если f> 0, то с помощью команды F2XM1 вычисляется 2* - 1. Если / < 0, то вначале вычисляется 2^ - 1 по команде F2XM1, а затем находится У - 1 по формуле У- 1=-[2ю- щг'-Я]. Шаг 3. Если 1 = 0, операция закончена. Если же i Ф 0, следует прибавить 1 к числу У - 1, полученному на шаге 2, затем умножить его на 21, пользуясь командой FSCALE, и, наконец, вычесть 1. На этом мы закончим рассмотрение трансцендентных команд процессора 80287. До перехода к последней группе команд покажем ситуации, в которых приходится учитывать, что 80286 и 80287 - это два отдельных процессора и иллюзия работы с одним процессором исчезает. 4.10. СОВМЕСТНАЯ РАБОТА ПРОЦЕССОРОВ 80286 и 80287 Процессоры 80286 и 80287 могут выполнять команды одновременно. Когда процессор 80286 встречает команду процессора 80287, он ожидает, пока процессор 80287 не освободится, разрешает процессору 80287 начать выполнение команды, сразу переходит к следующей команде. Процессор 80286 не ожидает, пока процессор 80287 закончит выполнение команды. Но если процессор 80287 не закончил свои действия, когда процессор 80286 встречает другую команду процессора 80287, то процессор 80286 будет ожидать (см. выше первое действие). Такой подход разрешает процессору 80286 продолжать выполнение программы, пока процессор 80287 занят "медленной" командой. Например, времени 159
выполнения команды FPATAN достаточно для того, чтобы процессор 80286 произвел около 500 операций сложения 16-битных целых чисел. Обычно программист не задумывается о параллельной работе процессоров, если ему, конечно, не нужна тонко настроенная программа. Благодаря ожиданию на первом шаге процессор 80286 гарантирует, что почти все программы дают такие же результаты, как будто работает один процессор, а не два. Но все же в двух редких случаях эта иллюзия исчезает. Предположим, что процессор 80287 должен запомнить что-то в памяти, а это что-то требуется процессору 80286, например FIST I MOV АХ,I Здесь процессор 80286 начнет выполнять команду MOV раньше, чем процессор 80287 закончит команду FIST, и в регистр АХ будет передано неправильное значение. Отметим, что этой проблемы не возникает, если есть промежуточные команды процессора 80287, например FIST I FADD ST(3) MOV AX,I Здесь процессор 80286, встретив команду FADD, должен ожидать завершения команды FIST. Однако промежуточные команды процессора 80286, например FIST I MOV J,BX MOV AX,I не помогают, если, конечно, они не задержат процессор 8U286 на время, достаточное процессору 80287 для завершения команды FIST. Команда FWAIT процессора 80286 (FWAIT и WAIT являются синонимами на языке ассемблера) заставляет его ожидать до тех пор, пока процессор 80287 не освободится. Следовательно, для решения проблемы нужно просто вставить команду FWAIT: FIST I FWAIT ; Ожидать,пока 802В7 закончит FIST MOV АХ,I В общем, задача синхронизации процессоров возникает, когда процессор 80287 обращается к ячейке памяти, к которой впоследствии обращается и процессор 80286. Во всех случаях она решается вставкой команды FWAIT между подозрительными командами процессоров 80287 и 80286. 160
Если процессор 80286 не подчиняется первому правилу, возникает "двоевластие". В микропроцессоре 8086 это правило игнорируется: ассемблер автоматически вставляет команду FWAIT перед каждой командой микропроцессора 8087. Эти дополнительные команды FWAIT не нужны для процессора 80286, поэтому ассемблер ASM286 их не генерирует. Вторая проблема синхронизации процессоров появляется в особых случаях. Если при выполнении команды процессора 80287 возникает незамаскированный особый случай, процессор 80286 может "уйти вперед" и выполнить много команд от момента начала "плохой" команды до момента обнаружения особого случая. Более того, процессор 80286 игнорирует особые случаи до тех пор, пока ему не встретится команда FWAIT или другая команда процессора 80287; только здесь распознается прерывание. Следовательно, в момент прерывания указатель команды уже не адресует ту команду процессора 80287, которая вызвала особый случай. Однако указатели особого случая в процессоре 80287 правильно идентифицируют "плохую" команду; собственно для этого указатели и были введены в процессор 80287. Параллельность в системах с процессорами 80286 и 80287 достигается и особенностями схемы самого процессора 80287. Как показано на рис. 4.18, а, он состоит из двух устройств: устройства шинного интерфейса ВШ и численного операционного устройства NEU. Первое из них взаимодействует с внешним миром (в частности, с процессором 80286) и содержит регистры управления и состояния. Второе содержит численные регистры и выполняет все вычисления. Устройство ВШ может реагировать на некоторые команды, как на "свои собственные", несмотря на то, что устройство NEU занято. Процессор 80286 4 ► Устройство шинного интерфейса [Регистр состояния I Регистр управления! 1 Численное операционное устройство Численные регистры Рис. 4.18. Взаимодействие процессоров 80286 и 80287 6 Заказ 6126 161
Например, в регистре состояния есть бит занятости Busy, который показывает, занято устройство NEU или нет. Процессор 80286 проверяет этот бит при выполнении команды FWAIT (и на первом действии, когда встречает команду процессора 80287). Команда FNSTSW процессора 80287 обращается к регистру состояния; ее ВШ выполняет без помощи NEU. Процессор не ожидает, когда он встречает команду FNSTSW. Следовательно, программист может использовать эту команду для проверки бита занятости, не заставляя процессор 80286 остановиться и ожидать процессор 80287. Другими словами, программа может инициировать продолжительную команду процессора 80287 и периодически проверять ход ее выполнения. Поведение процессора 80286 "без ожидания" при выполнении команды FNSTSW характерно для небольшой группы команд, которые может выполнять устройство ВШ. Все эти команды включены в последнюю группу - группу административных команд . 4.11. АДМИНИСТРАТИВНЫЕ КОМАНДЫ Команды этой группы, приведенные в табл. 4.17, обеспечивают доступ к нечисленным регистрам процессора 80287. Мнемоники, которые начинаются с FN, соответствуют командам "без ожидания", т.е. процессор 80286, передавая их для выполнения в процессор 80287, не проверяет занятость устройства ВШ. Кроме того, процессор 80286 игнорирует численные особые случаи, когда встречаются команды "без ожидания". Ассемблер формирует форму "с ожиданием", если программист опускает из мнемоники букву N (см. табл. 4.17). Это заставляет процессор 80286 реагировать на незамаскированные особые случаи и ожидать завершения действий устройства NEU перед выполнением команды "без ожидания", т.е. заставляет команду "без ожидания" действовать как обычная команда процессора 80287. В общем, программистам рекомендуется избегать форм "без ожидания", если не проведен тщательный анализ параллельной работы. Команда FNSTCW (FSTCW) передает содержимое регистра управления в ячейку памяти, а команда FLDCW загружает регистр управления из ячейки памяти. Эти две команды применяются для изменения режима работы процессора 80287 (режима округления, управления бесконечностью и др.). Процессор 80287 гарантирует, что эти две команды будут закончены, прежде чем процессор 80286 начнет выполнять следующую команду, поэтому рассмотренные выше проблемы синхронизации не возникают. Обычно эта группа называется командами управления процессором. — Прим, перев. 162
Таблица 4.17. Алминистративныекоманды Мнемоника команды Описание команды FNSTCW(FSTCW) Запомнить слово управления FLDCW Загрузить слово управления FNSTSW (FSTSW) Запомнить слово состояния FNSTSW АХ (FSTSW AX) Запомнить слово состояния в АХ FNCLEX (FCLEX) Сбросить особые случаи FNINIT (FINIT) Инициализировать процессор FNSTENV (FSTENV) Запомнить среду FLDENV Загрузить среду FNSAVE (FSAVE) Запомнить полное состояние FRSTOR Восстановить полное состояние FINCSTP Инкремент указателя стека FDECSTP Декремент указателя стека FFREE Освободить регистр FNOP Нет операции FSETPM Установить защищенный режим Команда FNSTSW (FSTSW) передает содержимое регистра состояния в ячейку памяти, а команда FNSTSW АХ (FSTSW АХ) передает его в регистр АХ процессора 80286. Мы уже пользовались командой FSTSW АХ в форме "с ожиданием" при рассмотрении условных переходов. Команды FSTSW АХ в микропроцессоре 8087 нет, поэтому в нем условные переходы реализуются сложнее. Гарантируется, что команды FNSTSW и FNSTSW АХ будут закончены до перехода процессора 80286 к следующей команде, поэтому и здесь проблем синхронизации нет. Команда FNCLEX (FCLEX) сбрасывает в регистре состояния флажки всех особых случаев и биты ES и Busy. Мы уже подчеркивали, что флажки особых случаев являются "накапливающими" и должны явно сбрасываться программистом; для этого и предназначена команда FNCLEX. Команда FNINIT (FINIT) инициализирует регистры управления, состояния и тэгов на значения, приведенные в табл. 4.18. Такое же действие производит аппаратный сигнал сброса (см. гл. 6). Команда запоминания среды FNSTENV (FSTENV) записывает в память содержимое регистров управления, состояния, тэгов и указателей особого случая. Формат этой информации в памяти показан на рис. 4.19. Затем команда FNSTENV инициализирует все регистры так же, как команда FNINIT. Команда FLDENV загружает среду, ранее запомненную командой FNSTENV. Эти команды применяются в процедурах обработки особых случаев, чтобы получить доступ к указателям особого случая.
Таблица 4.18. Инициализация процессора80287 Регистр процессора Режим работы процессора Регистр управления Режим бесконечности Режим округления Точность Все особые случаи Регистр состояния Не занят Код условия не определен ST = 0 ES = 0 Флажки особых случаев Регистр тэгов Проективный Округление к ближайшему Расширенная Замаскированы Все нулевые Все тэги показывают "пустой" Регистр управления Регистр состояния Регистр тэгов * Указатель команды " - Указатель операнда - —•Указатель команды— «..Указатель операнда— 16 бит Регистр управления Регистр состояния Регистр тэгов О 2 4 6 10 ST(0) ST(1) ST (2) ST(3) ST (4) ST (5) ST (6) ST (7) Рис. 4.19. Формат памяти в командах FSTENV и FLDENV 80 бит Рис. 4.20. Формат памяти в комарах FSAVE и FRSTOR 164
Команда FNSAVE (FSAVE) действует аналогично команде FNSTENV, но дополнительно она сохраняет в памяти содержимое численных регистров. Всего в память передается 94 байта (см. формат на рис. 4.20). Команда FRSTOR восстанавливает все регистры процессора 80287 из памяти. Эти команды в основном применяются для переключения задач (см. гл.5), поскольку запоминается/восстанавливается полное состояние процессора. Команды FINCSTP и FDECSTP осуществляют соответственно инкремент и декремент указателя стека ST. Они не влияют на регистр тэгов и численные регистры. Например, команда FINCSTP эквивалентна команде FSTP ST. Команда FFREE ST(/) устанавливает тэг регистра ST(/) на указатель "пустой". Команда FNOP действует так же, как команда FST ST, ST(0), т.е. не производит никакой операции. Команда установки защищенного режима FSETPM переводит процессор 80287 в виртуальный режим. После аппаратного сброса процессор 80287, как и процессор 80286, находится в реальном режиме. После выполнения команды РБЕТРМдля перевода процессора в реальный режим необходим аппаратный сброс. На этом мы заканчиваем обзор системы команд процессора 80287. ЗАКЛЮЧЕНИЕ После изучения систем команд процессоров 80286 и 80287 мы готовы к рассмотрению тех новых их возможностей, которых нет в предшествующих процессорах семейства 86. Они обсуждаются в гл. 5 вместе с теми новыми командами, которые поддерживают эти возможности. ГЛАВА 5 АРХИТЕКТУРА ПРОЦЕССОРА 80286 И ОПЕРАЦИОННАЯ СИСТЕМА 5.1. ВВЕДЕНИЕ В 60-х гг. были разработаны программные и аппаратные механизмы для разделения компьютера между группами пользователей. Их цель - создать одновременно у каждого пользователя иллюзию монопольного владения компьютером. Моделируемая копия компьютера каждого пользователя называется задачей, а создание и использование на компьютере нескольких задач называется мулътизадачностью. Один из i6S
способов реализации мультизадачное™, называемый разделением времени, заключается в разделении каждой секунды времени на кванты, в течение которых выполняются разные задачи. Именно благодаря разделению времени все пользователи одновременно уверены в том, что у каждого из них есть своя, хотя и более медленная, копия компьютера. Операционные системы, которые используют разделение времени, называются системами с разделением времени. Первоначально основной причиной разделения времени была высокая стоимость компьютеров по сравнению со стоимостью терминалов, т. е. один дорогой компьютер мог обслуживать многих пользователей, имеющих собственные дешевые терминалы. Сейчас же стоимость некоторых персональных компьютеров приближается к стоимости терминала, и этот аргумент в защиту разделения времени теряет свою силу. Но все же есть две дополнительные причины разделения центрального компьютера, которые действуют и в эру персональных компьютеров. Во-первых, центральный компьютер имеет оборудование, дублировать которое дорого, например быстродействующие и высококачественные принтеры, дисковые накопители большой емкости, ленточные накопители и сверхбыстрые центральные процессоры. Во-вторых, разделенный компьютер обеспечивает лучшую поддержку группе людей, работающих над совместным проектом. Центральный компьютер обеспечивает простое и управляемое разделение программ, работу с общей базой данных, взаимодействие между пользователями и обработку транзакций. Следовательно, возможность задач разделять информацию и взаимодействовать друг с другом приобретает все большее значение. По-видимому, в будущем удастся достичь разделения информации, объединяя персональные компьютеры в быстродействующую сеть. Пока же широкому распространению компьютерных сетей препятствует требование наличия дорогих линий связи, множество конкурирующих стандартов и несколько технических проблем (обычно объединяемых под названием "распределенная обработка"). Поэтому, несмотря на огромное число дешевых персональных компьютеров, разделенные компьютеры применяются довольно широко. Мультизадачность не ограничивается разделенными вычислительными системами. Она интенсивно используется в системах реального времени для управления станками. Здесь одна задача непрерывно собирает информацию от датчиков, вторая рассчитывает, как должно реагировать оборудование, а еще одна задача выдает управляющие воздействия. Дополнительные задачи воспринимают команды от персонала и индицируют в удобной форме информацию о состоянии оборудования. Мультизадачность проникает даже в персональные компьютеры. В интегрированных программных пакетах отдельные задачи выполняют 166
обработку текстов, расчеты по электронным бланкам, формируют графические изображения, работают с базой данных и обеспечивают взаимодействие. Каждой задаче на экране персонального компьютера выделяется своя область, или окно. Пользователь переключается с задачи на задачу, обращаясь от одного окна к другому. В таких интегрированных программных пакетах очень важно, чтобы пользователь мог передавать информацию между различными задачами. В этой главе мы рассмотрим те средства процессора 80286, которые помогают мультизадачным операционным системам, причем начнем со средств управления памятью. Они позволяют операционной системе изолировать задачи друг от друга; это очень важно для того, чтобы ошибка в одной задаче не вызвала краха всей системы. Управление памятью позволяет также выполнять программу в разных областях памяти без повторного ассемблирования или компилирования. Благодаря этому операционная система может оптимизировать использование памяти, перемещая задачи по мере их запуска и окончания. Управление памятью дает возможность организовать виртуальную память, т. е. способ выполнения программ, больших, чем имеющаяся основная память, посредством автоматической пересылки частей программы между дисковым накопителем и памятью. Виртуальная память полезна даже в том случае, если мультизадачность не применяется. Наконец, управление памятью обеспечивает управляемое разделение информации между задачами. Далее мы обсудим механизмы защиты процессора 80286. Эти средства позволяют операционной системе защитить себя и свою важную информацию от разрушения из-за ошибок или злого умысла некоторых задач. Даже в однопользовательских системах защита полезна для локализации последствий ошибок, чтобы критические подсистемы (например, дисковый ввод-вывод) не повреждались разрабатываемыми программами. Затем мы коснемся команд процессора 80286, предназначенных для переключения его с одной задачи на другую. В частности, системы реального времени требуют быстрого переключения задач. Благодаря аппаратной поддержке процессор 80286 сокращает время на переключение задач, что, естественно, увеличивает время для самих задач. Одной из возможных причин переключения задач является возникновение особого случая в выполняющейся задаче, а другой - сигнал прерывания от периферийного устройства, подключенного к процессору. Мы покажем, как процессор 80286 реагирует на особые случаи и прерывания и приведем список возникающих в нем особых случаев. Почти все средства процессора 80286, рассматриваемые в настоящей главе, отсутствуют в микропроцессоре 8086. Когда процессор 80286 работает в реальном режиме, он подавляет свои новые возможности и 167
имитирует поведение микропроцессора 8086. После операции сброса процессор 80286 начинает работать в реальном режиме. Для перевода его в виртуальный режим, в котором разрешены его новые возможности, выполняется программа инициализации. Поэтому глава завершается обсуждением инициализации системы. 5.2. УПРАВЛЕНИЕ ПАМЯТЬЮ Ключевым моментом управления памятью в процессоре 80286 является преобразование адреса, т. е. превращение адресов, используемых программами, в адреса, воспринимаемые схемами памяти. Напомним, что указатель в процессоре 80286 (и в микропроцессорах 8086, 8088 и 80186) имеет длину 32 бита. Он состоит из 16-битного селектора сегмента и 16-битного смещения в выбранном сегменте. Когда указатель находится в памяти, смещение содержится по меньшему адресу, а затем размещается селектор сегмента, адрес которого на 2 больше. Это соответствует соглашению о хранении более значимых величин по большим адресам. Но, как мы видели в гл. 2, наиболее часто два компонента указателя разделены, причем селектор находится в сегментном регистре, а смещение либо вычисляется в соответствии с режимом адресации операнда (прямая, база + смещение и др.), либо находится в регистре (IP, BP и др.) Этот принцип действует в реальном и виртуальном режимах работы процессора 80286. Однако оба режима различаются тем, каким образом из селектора и смещения образуется физический адрес памяти. Под физическим адресом мы понимаем то единственное число, которое передается в память для обращения к ней. Вычисление физического адреса в реальном режиме. Это действие уже рассматривалось в гл. 2, поэтому здесь мы только напомним о нем. В реальном режиме 32-битный указатель называется реальным адресом. На рис. 5.1. показано преобразование реального адреса в физический. Шестнадцатибитный селектор сдвигается на 4 бита влево (т. е. умножается на 16), определяя 20-битный базовый адрес сегмента. Затем к базовому адресу прибавляется 16-битное смещение и в результате получается 20-битный физический адрес. При сложении переполнение игнорируется. Несмотря на то, что 16-битные селектор и смещение допускают 216 сегментов по 216 байт, результирующее адресное пространство составляет на 216 х 216 = 232 (около 4 млрд.) байт, а всего 220 (около 1 млн.) байт. Объясняется это перекрытием сегментов. Например, два реальных адреса с парами (селектор, смещение) = (2, 5) и (1, 21) соответствуют одному и тому же физическому адресу 37. Чтобы получить большее адресное пространство, допускаемое 32-битным указателем, требуется другой способ преобразования адреса. 168
32 бита Реальный адрес I 16 бит Селектор I 16 бит Смещение 20 бит Базовый адрес Физический адрес 20 бит Рис. 5.1. Преобразование адреса в реальном режиме Вычисление физического адреса в виртуальном режиме. В виртуальном режиме 32-битный указатель называется виртуальным адресом. Он, как и реальный адрес, состоит из 16-битных селектора и смещения. По-прежнему селектор определяет базовый адрес сегмента, к которому для получения физического адреса прибавляется смещение. Но вместо образования базового адреса сегмента путем сдвига селектора влево на 4 бита базовый адрес получается посредством обращения (индексирования) к таблице в памяти, что показано на рис. 5.2. Селектор виртуального адреса состоит из трех полей: запрашиваемый уровень привилегий RPL, индикатор таблицы TI и индекс. Поле RPL используется операционной системой для решения рассматриваемой далее проблемы "троянского коня". Это поле на участвует в преобразовании адреса и обсуждается в разделе по кольцам защиты. Поле индикатора таблицы показывает, какая из двух таблиц привлекается для поиска базового адреса. Если TI = 0, то используется глобальная дескрипторная таблица GDT. Система с процессором 80286 имеет одну таблицу GDT, которая разделяется всеми задачами. Если TI = 1, то используется локальная дескрипторная таблица LDT, причем каждая задача имеет свою LDT. Следовательно, базовые адреса сегментов, разделяемых всеми 169
32 бита Индекс Глобальная дескрипторная таблица 24 бита Рис. 5.2. Преобразование адреса в виртуальном режиме задачами, хранятся в GDT,a базовые адреса "частных" сегментов каждой задачи находятся в LDT. Поле индекса селектора служит индексом выбранной таблицы. Каждый элемент таблицы называется дескриптором. Таблица индексируется с нуля и значение i поля индекса относится к /-му дескриптору в таблице. Дескрипторы имеют длину 8 байт и каждый из них содержит 24-битный базовый адрес соответствующего сегмента, т. е. он занимает 3 байта из 8. Назначение остальных байт дескриптора мы рассмотрим в дальнейшем. 170
Полученный из выбранного дескриптора 24-битный базовый адрес суммируется с 16-битным смещением, в результате чего получается 24-битный физический адрес. Переполнение при сложении игнорируется. Приведенное правило имеет одно исключение. Селектор с TI = 0 (т. е. селектор GDT) и индексом, равным нулю, считается пустым селектором. Пустые селекторы не обращаются к нулевому дескриптору в GDT. Пустой селектор разрешается загрузить в сегментный регистр, но любая попытка использовать его в преобразовании адреса вызывает в процессоре 80286 особый случай. Следовательно, пустые селекторы можно использовать как "держатели места". Например, перед запуском задачи операционная система может загрузить в регистры ES и DS пустые селекторы. Тогда использование задачей неинициализированных регистров ES или DS приведет к особому случаю. Сделаем несколько замечаний о процессе преобразования адреса. Прежде всего отметим, что физические адреса виртуального режима больше физических адресов реального режима (24 бита вместо 20). Поэтому в виртуальном режиме адресное пространство составляет 16М байт вместо Ш байт в реальном режиме. Далее, в виртуальном режиме сегменты могут не перекрываться, потому что базовые адреса в двух дескрипторных таблицах выбираются произвольно. Так как длина поля индекса в селекторе 13 бит, в дескрип- торной таблице может быть самое большее 213 дескрипторов, каждый из которых описывает сегмент до 216 байт. Таким образом, каждая задача может иметь свое "частное" адресное пространство до 213 х 216 = 229 (примерно 0,5 млрд) байт и может разделять со всеми другими задачами почти 229 байт. Полное адресное пространство задачи составляет 230 байт (1Г байт). Так как аппаратно-допустимая память составляет 16М байт, в тех ситуациях, когда ее не хватает, приходится привлекать методы организации виртуальной памяти и внешние запоминающие устройства (например, дисковые накопители). Основной прием заключается в том, чтобы в любой момент времени держать в основной памяти только несколько сегментов задачи. Операционная система должна пересылать сегменты с диска в память, когда они потребуются, и возвращать их на диск, освобождая память, когда сегменты больше не нужны. Далее мы покажем, как процессор 80286 помогает операционной системе в этих операциях. Отметим, наконец, что формат селектора в виртуальном режиме нарушает принцип "информация большей значимости находится по большему адресу". Поля TI и RPL занимают три младших бита 16-битного селектора, но значимость их больше, чем у индекса. Их следовало бы разместить в трех старших битах селектора. Неправильное размещение бит усложняет операции с 32-битными указателями, так как значение перено- 171
са из смещения в селектор ошибочно прибавляется к полю EPL, а не к индексу. Регистры преобразования адреса. На рис. 5.3 показаны регистры процессора 80286, участвующие в преобразовании адреса. Четыре сегментных регистра мы уже рассматривали в гл. 2. Регистры GDT и LDT предназначены для преобразования адреса только в виртуальном режиме. Регистр GDT содержит физический адрес таблицы GDT и ее длину (в байтах) без 1. Использование селектора, который при индексировании выходит за границу таблицы GDT, вызывает особый случай защиты. Обычно регистр GDT загружается при инициализации системы, а в дальнейшем не изменяется. Регистр GDT 24 бита База GDT 16 бит Предел GDT Регистр LDT 16 бит Селектор LDT 16 бит Сегментные регистры Рис. 5.3. Регистры, участвующие в преобразовании адреса Регистр LDT хранит селектор сегмента, содержащего текущую LDT. Этот селектор должен индексировать дескриптор в таблице GDT. Во время переключения задач процессор 80286 изменяет все локальное адресное пространство простой перегрузкой регистра LDT. Вот почему переключение задач реализуется очень быстро. Иса ль^вание в регистре LDT селектора, а не физического адреса (как в регистре GPT) позволяет обменивать локальные дескрипторные таблицы с внешней памятью теми же самыми механизмами виртуальной памяти, которые применяются для всех сегментов. Если бы в преобразовании адреса участвовали только эти регистры, оно производилось бы очень медленно. Для обращения к байту в памяти потребовались бы следующие шаги: CS DS SS ES 172
1. Взять селектор из сегментного регистра. 2. Если бит TI показывает на глобальную дескрипторную таблицу, взять из регистра GDT адрес дескрипторной таблицы и перейти к шагу 4. 3. Если бит TI показывает на локальную дескрипторную таблицу, то а) взять селектор сегмента LDT из регистра LDT; б) выделить в селекторе поле индекса и умножить индекс на 8 (сдвинуть влево на три бита). Это действие объясняется тем, что длина каждого дескриптора составляет 8 байт; в) прибавить результат к адресу GDT из регистра GDT; г) считать из памяти адресуемый дескриптор; д) выделить из этого дескриптора базовый адрес сегмента, содержащего таблицу LDT. Полученный базовый адрес является адресом дескрипторной таблицы. Перейти к шагу 4. 4. Выделить значение из поля индекса селектора, умножить его на 8 и прибавить к адресу дескрипторной таблицы. Считать из памяти дескриптор по этому адресу. 5. Выделить из дескриптора базовый адрес сегмента. 6. Прибавить значение смещения к базовому адресу сегмента. Это и будет требуемый физический адрес. 7. Осущствить обращение к памяти по физическому адресу. Таким образом, обращение к байту в памяти дополнительно требует (в случае LDT) обращений к двум 8-байтным дескрипторам в памяти и выполнения двух 24-битных сложений. (Операции сдвига и выделения не расходуют времени благодаря специальной маршрутизации "проводников" на кристалле.) Только сравните приведенные выше действия с единственным 20-битным сложением без дополнительных обращений к памяти в реальном режиме! К счастью, это еще не все. Отметим прежде всего, что обращения к памяти производятся гораздо чаще, чем изменения сегментных регистров и переключения задач. Поэтому целесообразно ускорить обращение к памяти за счет замедления загрузки сегментных регистров и переключения задач. Такой компромисс достигается путем ассоциирования с регистром LDT и каждым сегментным регистром так называемого теневого регистра. Теневые регистры невидимы программам. Они автоматически модифицируются и невидимо используются схемами процессора 80286. На рис. 5.4 показаны все регистры преобразования адреса и их теневые регистры. Когда селектор загружается в сегментный регистр, соответствующий дескриптор автоматически загружается в его теневой регистр. Это действие эквивалентно приведенным выше шагам 1-4. Поскольку теперь дескриптор находится в теневом регистре, в последующих обращениях в 173
Регистр GDT 24 бита База GDT 16 бит Предел GDT Регистр LDT 16 бит Селектор LDT Дескриптор для LDT 16 бит Дескриптор для CS : Дескриптор для DS | Дескриптор для SS • f Дескриптор для ES Рис. 5.4. Регистры преобразования адреса и их теневые регистры память с использованием сегментного регистра нужно выполнить только шаги 5-7. Следовательно, время обращения к памяти в виртуальном режиме эквивалентно времени обращения к памяти в реальном режиме. Когда происходит переключение задач и регистр LDT изменяется, соответствующий новой LDT дескриптор автоматически загружается в теневой регистр регистра LDT (это соответствует приведенным выше шагам За - Зг). Следовательно, когда в дальнейшем селектор загружается в сегментный регистр, шаги За - Зг выполнять не нужно. Поэтому при перезагрузке сегментного регистра для модификации теневого регистра потребуются одно 24-битное сложение и одно обращение к памяти за дескриптором. Если программа редко модифицирует свои сегментные регистры, то в виртуальном режиме она будет выполняться примерно с такой же скоростью, как и в реальном. Если же она часто изменяет сегментные регистры, то в виртуальном режиме она выполняется значительно медленнее, чем в реальном. На практике наибольшее замедление получается когда программа должна проследить длинную цепь 32-битных указателей, так как при этом сегментный регистр должен изменяться для каждого обращения к памяти. Например, следующая программа вычисляет длину цепи элементов в предположении, что каждый элемент начинается с указателя на следующий элемент и что последний элемент начинается с указателя, имеющего нулевой селектор: 174 Сегментные регистры cs DS "is" ES
MOV CX.0 5 CX будет содержать длину LDS BX.chain ; Передать в DS:BX адрес первого элемента getnext: LDS BX,CBX3 ; Адрес следующего элемента в DS.BX MOV AX,DS ; Передать селектор в АХ AND AX, AX 5 Установить флажки по АХ LOOPNZ getnext Декремент СХ, перейти, если АХ не 0 NEG CX Образовать в СХ длину Эта программа в реальном режиме выполняется на 75 % быстрее, чем в виртуальном. Единственной командой, время выполнения которой различается в двух режимах, является команда LDS: в реальном режиме она выполняется за 7 циклов процессора, а в виртуальном режиме - за 21 цикл. Дополнительное время требуется для загрузки теневого регистра DS и контроля нарушения защиты. Таким образом, в виртуальном режиме процесс преобразования адреса превращает виртуальный адрес в физический. Виртуальный адрес состоит из селектора, по которому процессор может определить адрес базы сегмента, и значения смещения, которое после суммирования с базовым адресом дает физический адрес. Селектор определяет соответствующий базовый адрес сегмента путем обращения к таблице. Один бит селектора показывает, нужно ли использовать таблицу, глобальную для всей системы, или локальную для текущей задачи. Селектор также содержит индекс, который применительно к выбранной таблице позволяет получить дескриптор сегмента. Этот дескриптор содержит адрес базы выбранного сегмента. Следовательно, каждый селектор обращается к соответствующему дескриптору, а дескриптор - к соответствующему сегменту. Чтобы ускорить обращение к памяти, при загрузке селектора в сегментный регистр соответствующий дескриптор автоматически загружается в теневой регистр этого сегментного регистра. Дальнейшее обращение к памяти через сегментный регистр не требует обращений к таблице, так как базовый адрес выбранного сегмента находится в теневом регистре. Дескриптор сегмента. Формат дескриптора сегмента показан на рис. 5.5. Дескрипторные таблицы могут содержать дескрипторы не только сегментов; другие дескрипторы отличаются по формату от дескрипторов сегментов и рассматриваются далее. Однако всегда два старших байта каждого дескриптора не используются процессором 80286 и должны содержать нули, чтобы обеспечить совместимость с его будущими модификациями. Следующее поле в дескрипторах всех типов занимает байт доступа, информация в котором зависит от типа дескриптора. Байт доступа закодирован так, что путем его анализа всегда можно однозначно определить тип дескриптора. В дескрипторах сегментов следующее 175
поле базы содержит 24-битный базовый адрес сегмента. Младшие 16 бит дескриптора сегмента занимает поле предела, показывающее размер сегмента. Мы уже рассмотрели назначение поля базы, поэтому остановимся подробнее на байте доступа и поле предела. 16 бит 8 бит 24 бита 16 бит 0 Доступ База Предел Рис. 5.5. Формат дескриптора сегмента Кодирование байта доступа дескрипторов сегментов показано на рис. 5.6. Его формат зависит от того, является ли сегментом кода (т. е. исполняемым), сегментом данных (т. е. неисполняемым) или сегментом, содержащим LDT. 7 5-6 4 3 2 1 0 .,ГрГ»П1|1|о|я|а| 7 5-6 4 3 2 1 0 JM DPt |1|0|ED|W|A| Рис. 5.6. Форматы байта доступа сегментов: 7 5-6 4 0-3 II ■ ■ | а — для сегмента кода, о — для сегмента Р I DPL | 0 I 0 0 1 О I данных,в-длясегмента LDT Каждый байт доступа имеет двухбитное поле уровня привилегий дескриптора DPL. Оно позволяет операционной системе предотвратить доступ обычным пользовательским программам к сегментам операционной системы. Поле DPL подробно рассматривается в разделе по кольцам защиты. Два бита присутствия (наличия) Р и обращения А помогают операционной системе реализовывать виртуальную память. В системе виртуальной памяти при выполнении программы в памяти одновременно находятся только некоторые ее сегменты, а остальные хранятся на диске. Когда программа пытается обратиться к сегменту, которого в памяти нет, возникает особый случай и процедура его обработки считывает ("вталкивает") в память нужный сегмент. Если места в памяти для сегмента недостаточно, процедура обработки должна вначале освободить требуемое место, пересылая ("выталкивая") один или несколько сегментов из памяти на диск. Весь этот процесс называется свопингом ("подкачкой"). После передачи нужного сегмента в память процедура обработки особого случая осуществляет возврат в программу и она продолжается, не подозревая о прерывании. Следовательно, операционная система создает иллюзию того, что все сегменты программы все время находятся в памяти. 176
Бит присутствия Р показывает, находится сегмент в памяти или на диске. Если в дескрипторе Р = 0, то любая попытка использовать этот дескриптор (например, загрузкой селектора в сегментный регистр) вызывает особый случай "неприсутствия" (т. е. отсутствия). Операционные системы виртуальной памяти отмечают все сегменты, находящиеся в памяти, установкой Р = 1, а сегменты, которых в памяти нет (они на диске), установкой Р = 0. Следовательно, особый случай "неприсутствия" возникает, когда программа обращается к сегменту, которого нет в памяти. Процедура обработки этого особого случая может считать нужный сегмент с диска, установить поле базы дескриптора на адресацию базы сегмента и сделать Р = 1. После возврата в программу она продолжается так, как будто ничего не произошло. В отличие от бита Р, который сообщает операционной системе, когда передавать сегмент в память, бит обращения А помогает ей выбрать сегмент, который передается на диск, когда в памяти нет достаточного места для нового сегмента. При выборе сегмента операционная система попытается избежать передачи на диск тех сегментов, которые потребуются в ближайшем будущем. Поскольку она не может точно предсказать будущее поведение программы, операционная система должна сделать обоснованное предположение. Наверное, можно согласиться с тем, что не используемый долгое время сегмент, вероятно, не потребуется и в будущем. Поэтому приемлемая стратегия при необходимости освобождения пространства памяти заключается в том, чтобы обменять (swap) сегмент, к которому не было обращений самое продолжительное время. Эта стратегия называется LRU - заменяется дольше всего не использовавшийся сегмент. Бит А помогает операционной системе ассоциировать с каждым сегментом приблизительное время его последнего использования. Он не влияет на выполнения программы, но автоматически устанавливается процессором 80286 в 1 (в дескрипторной таблице), когда селектор для его дескриптора загружается в сегментный регистр. Сброс бита А в нуль осуществляет только программа. Поскольку перед доступом к сегменту необходимо загрузить сегментный регистр, бит А приблизительно показывает, было ли обращение к сегменту после того, как программа последний раз сбросила бит А в нуль. Биты А всех сегментов первоначально должны находиться в состоянии нуль. Через регулярные интервалы времени операционная система сканирует все дескрипторы и сбрасывает их биты А в нуль. Если во время сканирования обнаруживаются дескрипторы с битами А = 1, то к ним после предыдущего сканирования были обращения. Операционная система должна произвести инкремент времени последнего использова- i ния таких сегментов. Когда необходимо выбрать сегмент для передачи 177
на диск, им будет сегмент с наименьшим временем последнего использования. Бит 1 байта доступа для сегмента кода называется битом считываемого R сегмента. Если R = 1, то наряду с обращением к такому сегменту для выполнения разрешаются и обращения для считывания. Если же R « О, любая попытка считать из сегмента вызывает особый случай {особый случай защиты). Любая попытка записать в исполняемый сегмент приводит к особому случаю защиты (это обычная реакция процессора 80286 на команды, которые пытаются нарушить ограничения защиты). Далее мы будем показывать все ситуации, которые приводят к особым случаям защиты. Бит 1 байта доступа для (неисполняемого) сегмента данных является битом разрешения записи W. Если W = 1, то помимо считывания из сегмента данных разрешена и запись в него. Если же W = 0, то любая попытка записать в сегмент вызывает особый случай защиты. Кроме того, любая попытка выполнить сегмент данных также приводит к особому случаю защиты. С сегментами LDT нельзя явно производить операции считывания, записи или выполнения. Их можно использовать только неявно в процессе преобразования адреса. Далее, в регистр LDT можно загружать только селекторы для сегментов LDT. Следовательно, для создания локальной дескрипторной таблицы операционная система должна вначале создать в таблице GDT дескриптор для сегмента данных, затем записать дескрипторы в этот сегмент данных и после этого изменить дескриптор сегмента данных в дескрипторе LDT. Причина того, что в процессоре 80286 не разрешает (пользовательской) программе анализировать свою локальную дескрипторную таблицу, заключается в том, что невозможно поставить поведение программы в зависимость от конкретных физических адресов (в отличие от виртуальных адресов) ее сегментов. Благодаря этому операционная система может свободно перемещать сегменты в памяти путем изменения полей базы их дескрипторов. Программы, работающие с этими сегментами, и не "подозревают" о перемещениях сегментов. Ограничения на доступ по считыванию, записи и выполнению помогают поддерживать целостность системы, обнаруживать ошибки и предотвращать программное "пиратство" в многопользовательских системах. В системе виртуальной памяти сегмент с запрещенной записью не нужно физически передавать на диск (если его все же нужно удалить из памяти), так как после передачи с диска он не изменялся. Следовательно, ограничения на доступ по записи повышают эффективность системы виртуальной памяти, сокращая дисковые передачи. 178
Упомянем здесь про бит, которого нет в дескрипторах сегментов, потому что его можно реализовать программно, а не аппаратно. Во многих компьютерах с системой виртуальной памяти предусматривается так называемый бит модификации, показывающий, была ли действительная запись в сегмент с разрешенной записью после его передачи с диска. Если такой сегмент удаляется из памяти и он не был модифицирован, то физически записывать его на диск нет необходимости. По существу, эта ситуация аналогична сегменту с запрещенной в него записью. Эффекта наличия бита модификации можно достичь, если отмечать как незаписываемые (устанавливая W = 0) все сегменты при передачах их с диска. Когда программа первый раз попытается записать в сегмент, возникнет особый случай защиты. Процедура его обработки может определить, является ли запись недопустимой, анализируя специальные таблицы. Если запись в сегмент разрешена, процедура отмечает сегмент как записываемый (устанавливает W = 1) и осуществляет возврат в программу. Это предотвращает возникновение последующих особых случаев и сообщает программам виртуальной памяти, что сегмент модифицировался после своей последней передачи с диска в память. Бит 2 байта доступа для сегмента кода является битом подчинения С (о нем речь пойдет при рассмотрении колец защиты). Бит 2 байта доступа для сегмента данных называется битом расширения вниз ED. Он применяется для отметки сегментов стека, которые расширяются вниз (в область меньших адресов). Бит ED управляет интерпретацией поля предела дескриптора. Во всех типах сегментов, за исключением сегментов данных с ED = 1, поле предела содержит длину сегмента (в байтах) минус 1. Следовательно, допускаются сегменты с длиной от 1 до 216 (64К) байт. При обращениях к памяти процессор 80286 контролирует, чтобы используемые смещения были меньше или равны пределу; для контроля применяются 16-битные беззнаковые сравнения. Контроль осуществляется параллельно с преобразованием адреса, поэтому дополнительное время на него не расходуется. Если смещение оказывается больше предела, фиксируется особый случай. Сегменты данных с ED = 1 (т. е. сегменты с расширением вниз) предназначены для стеков. Напомним, что в процессоре 80286 стеки начинаются со смещения FFFF и "растут" вниз к меньшим адресам, поэтому приведенная выше интерпретация поля предела для стеков не подходит. Если ED = 1, процессор 80286 контролирует, чтобы смещения были строго больше предела. Если же смещение меньше или равно пределу, регистрируется особый случай. На рис 5.7 показано сравнение обычных сегментов и сегментов с расширением вниз, причем сплошные линии ограничивают допустимые части сегментов. Отметим, что бит ED не влияет на то, 179
каким образом поле базы дескриптора участвует в процессе преобразования адреса. Изменяется только контроль нахождения в пределах. Физические адреса Смещения ТППШП 65.536-FFFF L • 65,534-FFFE База — Предел База- | Предел + 1 Предел Предел - 1 а) Физические адреса База — + 65.536 Смещения База—Ч Предел + 1 65,535-FFFF 66,534-FFFE Предел + 2 Предел + 1 Предел База—#>|J". "•2 j 1 •О б) Рис. 5.7. Размещение сегментов в памяти: a — сегмент с расширением вверх, б — сегмент с расширением вниз Контроль необходим для изолирования задач, так как без него задача может "выйти" из своих сегментов и исказить другую задачу. Такая ситуация может возникнуть, например, если программа индексирует элементы, находящиеся за концом массива. Разработчики процессора 80286 могли бы обойтись без пределов, потребовав, чтобы длина всех сегментов была точно 64К байт. Однако это привело бы к неэффективному использованию памяти и не позволило бы реализовать очень малые системы и системы с большим числом небольших задач. Особенно важен контроль пределов сегментов с расширением вниз, т. е. сегментов стеков. Операционная система может запустить задачу с небольшим стеком. Если задача превышает свой стек, возникает особый случай и процедура его обработки может распределить задаче большее стековое пространство. Таким образом, память не расходуется впустую на стеки, размеры которых определялись бы с запасом. Как видно, дескриптор сегмента содержит значительную информацию о соответствующем сегменте: 1. Поля базы и предела определяют местонахождение и размер сегмента. Бит ED сообщает, как интерпретировать поле предела. 2. Биты Р и А помогают программам виртуальной памяти решать, какие сегменты передавать на диск, а какие с диска. 3. Сегменты кода, данных и LDT различаются кодированием байта доступа, что позволяет процессору 80286 запретить запись в сегменты кода и (неявные) считывание и запись в сегменты LDT. 180
4. Биты W и R применяются для реализации дальнейших ограничений на способы доступа к сегментам. 5. Поле уровня привилегий дескриптора DPL и бит С используются для защиты сегмента от несанкционированных обращений (см. далее). Структуры разделения между задачами. Обычное базовое структурирование адресных пространств процессора 80286 показано на рис. 5.8, а. 1адача2^—р>| LPT2 | За^ачТ^—»| GOT |4-Задача2^ LDTA f#—Задачаа^) ЗадачаВд)—1ДТВ {^-ЗадачаВг) GOT От всех задач в) а^аС^—^ ШТС ^За^ачаС^ Рис. 5.8. Структуры разделения и изолирования задач: а - задачи изолируются отдельными LDT, б - задачи разделяют одно и то же адресное пространство, в — задачи организованные в задания (группы) 181
Каждая задача имеет свою локальную дескрипторную таблицу, и все задачи пользуются общей глобальной дескрипторной таблицей. В результате сегмент доступен либо только одной задаче, либо всем задачам. Однако возможны и другие структуры разделения. В однопользовательской системе, где не требуется изолирование задач, можно поместить пустой селектор в регистр LDT для каждой задачи. Тогда каждая задача разделяет одно и то же адресное пространство (см. рис. 5.8, б), и операционная система может не строить никаких LDT. Такой вариант удобен для небольших систем реального времени, а также перевода систем на базе микропроцессора 8086 в виртуальный режим. Группа взаимосвязанных задач может использовать одну и ту же LDT. Все задачи в группе будут разделять одно и то же адресное пространство, но группа, как целое, обладает частными сегментами, изолированными от остальной системы. Такая группа задач иногда называется "отрядом задач" или заданием. На рис. 5.8,в показана система, содержащая три задания. Этот вариант, по-видимому, оказывается простейшим способом реализации таких языков программирования, как Ада, в которых одна программа может состоять из нескольких задач. Здесь нужно просто реализовать каждую программу на языке Ада как задание. Более сложные структуры разделения и изолирования можно построить, обеспечив каждую задачу ее собственной LDT, но допустив несколько дескрипторов для каждого сегмента. Чтобы разделить сегмент между двумя задачами, каждая задача должна иметь идентичный дескриптор сегмента в своей LDT (см. рис. 5.9). Два дескриптора одного и того же Задача Задача Регистр LDT LDT 1 —«■ LOT Регистр LDT 182 Разделенный сегмент Рис. 5.9. Разделение сегмента с помощью альтернативного именования
сегмента называются альтернативными именами. Конечно, не обязательно ограничиваться только двумя задачами и двумя альтернативными именами; можно использовать любое число альтернативных имен. Альтернативное именование дает разработчику операционной системы две возможности. Во-первых, задачи можно снабдить дескрипторами только тех сегментов, о которых они должны знать. Размещение дескрипторов разделяемых сегментов в GDT делает их видимыми всем задачам,а альтернативное именование может ограничить сегменты для любой группы задач. Во-вторых, большие сообщения можно эффективно передавать между задачами, передавая просто дескрипторы сегментов с альтернативными именами, а не копию всего сообщения. Дополнительную информацию о передаче сообщений см. в приложении Д. Альтернативное именование позволяет также реализовывать сегмент, являющийся исполняемым и записываемым: одно имя делается дескриптором сегмента кода, а другое - дескриптором сегмента данных. Оба альтернативных имени помещаются в одну и ту же LDT. После этого программа может, выбирая соотвествующий дескриптор, выполнять сегмент и записывать в него. Альтернативное именование является наиболее мощным методом разделения сегментов в процессоре 80286, но оно приводит к трудностям для систем виртуальной памяти. Если альтернативное именование разрешено, то для передачи сегмента на диск операционная система должна проследить все альтернативные имена этого сегмента и сбросить их биты Р в нуль. Аналогичные трудности возникают и при передаче сегмента с диска. Простейший способ найти все альтернативные имена сегмента - просмотреть все LDT в системе. Этот способ успешно реализован во многих компьютерах, работающих, как и процессор 80286, с дескрипторами. Расходуемое на такой просмотр время зависит от особенностей системы. Более быстрый способ заключается в объединении всех альтернативных дескрипторов сегмента в список, причем так, что последний элемент списка показывает на первый. Тогда по заданному дескриптору легко находятся все его альтернативные имена. Список легко модифицировать по мере создания и уничтожения альтернативных имен. Единственная трудность заключается в том, где разместить четырехбайтные указатели, связывающие альтернативные имена. Простейшее решение - хранить связь в LDT сразу после дескриптора. Но при этом дескрипторы будут содержаться только в четных элементах LDT, а в нечетных будут находиться связи. Остающиеся четыре байта в нечетном элементе системные программы могут использовать как угодно, но разряд, соответствующий 183
биту Р, должен быть в состоянии 0, чтобы предотвратить использование элемента в преобразовании адреса. Один из недостатков рассмотренного способа состоит в том, что локальные дескрипторные таблицы, содержащие альтернативные имена, нельзя передавать на диск (или, точнее, связанные альтернативными именами локальные дескрипторные таблицы должны передаваться вместе). Второй недостаток заключается в уменшении числа дескрипторов, которые можно хранить в LDT, с 8192 до 4096. Системотехники, которым рассмотренный способ не подходит, могут хранить связи вне LDT, а в простейших системах виртуальной памяти полностью забыть об альтернативных именах. Команды управления памятью. В процессоре 80286 введено несколько команд, которых нет в микропроцессорах 8086 и 80186, для манипуляций регистрами преобразования адреса и анализа содержимого дескрипторов. Эти команды показаны в табл. 5.1. Таблица 5.1. Команды управления памятью Мнемоника команды Описание команды LGDT Загрузить регистр GDT SGDT Запомнить регистр GDT LLDT Загрузить регистр LDT SLDT Запомнить регистр LDT LAR Загрузить права доступа LSL Загрузить предел сегмента Команда LGDT загружает регистр GDT из памяти, причем при загрузке передаются шесть байт. Первые пять из них помещаются в регистр GDT в формате, показанном на рис. 5.3, а последний байт (с наибольшим адресом) процессор 80286 игнорирует. Однако для совместимости с будущими разработками последний байт должен содержать нули. Команда LGDT применяется только при инициализации системы. Команда SGDT передает содержимое регистра GDT в адресуемую область памяти. Запоминаются шесть байт, в пяти из которых находится содержимое регистра GDT, а последний байт не определен. Такая команда требуется в системных отладчиках для определения полного состояния процессора. Команда LLDT загружает свой операнд-слово из регистра или памяти в регистр LDT. Предполагается, что операнд является селектором GDT, показывающим на дескриптор LDT. Допускаются также пустые селекторы. Обычно для изменения регистра LDT применяется не команда LLDT, используемая только при инициализации системы, а команды переключения задач (см. далее). 184
Команда SLDT передает содержимое регистра LDT в операнд-слово, которым может быть регистр или память. В отличие от предыдущих команд она применяется не только в отладчиках и при инициализации. Пример ее использования дается далее при рассмотрении разделения времени. Напомним, что процессор 80286 запрещает операции считывания и записи в сегментах LDT, так как в полях базы дескрипторов находятся физические адреса. Но иногда желательно проанализировать и другую информацию в дескрипторах, а не только поле базы. Для этого предназначены следующие две команды. Команда LAR имеет два операнда. Второй операнд ее (слово в памяти или регистре) считается селектором. Команда загружает в свой первый операнд (регистр длиной в слово) байт доступа дескриптора, выбираемого вторым операндом. Байт доступа занимает старший байт первого операнда, а младший байт сбрасывается в нуль. Даже если селектор оказывается недействительным, команда LAR не вызывает особого случая защиты, а просто сбрасывает флажок нуля. При успешной загрузке флажок нуля устанавливается в 1. Команда LSL действует аналогично команде LAR, но загружает в свой первый операнд значение поля предела выбранного дескриптора, а не байт доступа. Не забывайте, что интерпретация поля предела зависит от байта доступа. Команда LGDT допускается в реальном режиме, чтобы программа инициализации системы могла инициализировать регистр GDT в реальном режиме до перехода в виртуальный режим. Команда SGDT также допустима в реальном режиме, будучи предназначенной для отладчиков. Команды LLDT, SLDT, LAR и LSL в реальном режиме вызывают особый случай недействительного кода операции. 5.3.К0ЛЬЦА ЗАЩИТЫ В предыдущем разделе мы рассмотрели, как процессор 80286 защищает одну задачу от другой, а теперь обратимся к тому, как он помогает операционной системе защитить саму себя. Предполагается, что процессор работает в виртуальном режиме. Защита требует наложения на обычные программы (не входящие в операционную систему) трех типов ограничений: 1) обычным программам запрещается выполнять некоторые команды. 2) обычным программам должны быть недоступны определенные сегменты, доступные операционной системе; 3) должно быть невозможным получение привилегии операционной системы кроме как входом в нее в разрешенной точке входа. 185
Рассмотрим, например, защиту средств управления памятью из разд. 5.2. Пределы сегментов соотносят задачу с теми сегментами, для которых она имеет дескрипторы. Эти дескрипторы находятся в GDT и в LDT задачи. Бели дескрипторы в GDT и LDT считаются "безопасными", то для нарушения защиты задача должна образовать другие дескрипторы. Один из способов заключается в использовании команды LGDT для загрузки регистра GDT и изменения базы GDT на ту область памяти, в которую задача может записывать. Следовательно, обычным программам необходимо запретить пользоваться командой LGDT. Для доказательства необходимости ограничения 2 замечаем, что операционная система должна иметь доступ к таблице GDT с правом записи, чтобы создавать дескриптор таблицы LDT для новых задач. Но мы только что видели, что обычным программам необходимо запретить запись в GDT. Для удовлетворения ограничения 2 можно использовать изолирование задач. Обслуживания операционной системы реализуются как отдельные задачи, в которых дескрипторы всех "чувствительных" сегментов (например, записываемый сегмент, содержащий таблицу GDT) находятся только в таблице LDT задачи обслуживания. Пользовательские программы могут запросить обслуживание операционной системы путем переключения задач. Но у такого подхода имеются следующие недостатки: . 1. Хотя переключение задач в процессоре 80286 реализуется относительно быстро, на него все же уходит времени в семь раз больше, чем на вызов. 2. При переключении задач трудно передавать параметры. Например, виртуальные адреса, содержащие селекторы LDT, имеют различное отображение в задачах с разными LDT. 3. Процедуры обработки особых случаев часто требуют доступа к адресному пространству задачи, вызвавшей особый случай, чтобы установить его причину. Доступ оказывается затрудненным, если процедуре обработки особого случая требуется своя LDT. В силу этих причин в процессоре 80286 реализован такой метод расширения и сокращения набора доступных сегментов, который опирается на вызов процедуры, а не на переключение задач. Обратимся к рассмотрению этого метода. Уровни привилегий. Правительство США использует следующий способ защиты важных документов. Каждому документу назначается один из четырех классов: совершенно секретный, секретный, конфиденциальный и неклассифицируемый. Чем важнее документ, тем выше класс. Каждый человек имеет один из четырех уровней благонадежности: совершенно секретный, секретный, конфиденциальный и неблагонадеж- 186
ный. Больший уровень благонадежности отражает большее доверие к человеку. Человеку с конкретным уровнем благонадежности разрешается доступ к документам соответствующего или меньшего класса. Например, человеку с уровнем благонадежности "секретный" разрешен доступ к секретным, конфиденциальным и неклассифицируемым документам, но запрещен доступ к совершенно секретным документам. Правительственная схема классификации показана на рис. 5.10. Каждая круговая полоска или кольцо соответствует совокупности документов, имеющих показанную классификацию (внутренний круг считается кольцом, хотя он ничего не окружает). Таким образом, документы во внутренних кольцах более защищены, чем во внешних, а человеку с конкретным уровнем благонадежности доступны документы в соответствующем кольце и во всех окружающих кольцах. Для защиты сегментов в процессоре 80286 применяется аналогичная схема. Вместо четырех классов и уровней благонадежности в нем действуют четыре уровня привилегий с номерами 0-3. Чем меньше номер уровня, тем более привилегирован этот уровень (в отличие от того, что вы ожидали). Уровни привилегий находятся в трех различных местах: 1. Каждый дескриптор имеет уровень привилегий дескриптора DPL. Для записываемых или считываемых сегментов дескриптор DPL можно считать "классом" сегмента, а для исполняемых сегментов DPL оказывается "уровнем благонадежности" процедур в этом сегменте. 2. Каждый селектор содержит запрашиваемый уровень привилегий RPL (см. далее). 3. Процессор 80286 автоматически поддерживает текущий уровень привилегий CPL. Он показывает "уровень благонадежности" текущей выполняющейся программы и равен содержимому поля DPL сегмента, который адресует регистр CS. Процессор хранит содержимое поля CPL в двух младших битах CS, замещая поле RPL хранимого там селектора. Чтобы обратиться к сегменту для считывания или записи, программа должна загрузить в сегментный регистр селектор этого сегмента. Представляется, что программа может обратиться к любому сегменту (даже более привилегированному, чем она сама), просто создавая для него селектор и загружая селектор в сегментный регистр. Для предотвраще- Рис. 5.10. Классификация информации правительством США
ния этого процессор 80286 генерирует особый случай защиты, если текущая программа (CPL) менее привилегирована, чем сегмент (DPL), т. е. если "уровень благонадежности" программы меньше класса сегмента. Другими словами, доступ разрешается, если DPL > CPL. Далее мы увидим, что программа может использовать поле RPL для временного уменьшения привилегий. Поэтому правило, разрешающее доступ, приобретает вид DPL > max (CPL, RPL). Напомним, что чем меньше номер, тем выше привилегии. Меньший уровень DPL означает более защищенный сегмент, а меньший уровень CPL означает более привилегированную программу. Разработчики процессора 80286 посчитали, что уровень привилегий 1 должен использоваться большей частью операционной системы. Уровень 0 предназначен для той небольшой части операционной системы, которая осуществляет управление памятью, защиту и управление доступом. Эта часть называется ядром безопасности. В ядре сосредоточены все функции безопасности, и оно защищено от остальной части операционной системы. Ядро безопасности должно быть разработано так, что если оно полностью безопасно, то и вся операционная система безопасна. Безопасность должна сохраняться, несмотря ни на какие повреждения в тех частях one рационной системы, которые находятся вне ядра. Подобные средства безопасности играют существенную роль в многопользовательских системах, имеющих дело с важной информацией; примерами служат правительственная деятельность, банковское дело и электронные системы расчетов. Разработка операционной системы с небольшим ядром безопасности обеспечивает несколько преимуществ. Например, для повышения уверенности в корректности программ ядра можно применить дорогие методы, которые неэкономичны для больших программ. Эти методы предполагают интенсивное тестирование ядра, анализ его целыми группами программистов и математические доказательства корректности программ. Если операционная система занимает два уровня, а пользовательские программы выполняются еще на одном уровне, то всего получаются три уровня. Откуда же взялся четвертый? Наверное, основная причина появления в процессоре 80286 четырех уровней заключается в том, что для кодирования трех уровней нужно два бита и четвертая комбинация оказалась свободной. Если назначить пользовательским программам уровень 3, то между пользователем и операционной системой остается уровень 2. Хорошими кандидатами для этого уровня оказываются специализированные подсистемы программирования, которые необходимо защищать, потому что они реализуют собственные механизмы безопасности. Примерами подобных подсистем служат системы управления базами данных, автоматизации управленческой деятельности и разработки программ, ш
На рис. 5Д1 показано использование четырех уровней привилегий. Из-за круговой диаграммы (сравните с рис. 5.10) уровни привилегий иногда называют кольцами защиты. Системотехники не обязаны работать со всеми четырьмя уровнями только потому, что они существуют. Простую незащищенную систему можно реализовать целиком в кольце 0. Традиционная система "пользователь/супервизор" реализуется с двумя кольцами: супервизору (т.е. операционной системе) отводится коль- Рис. 5.11. Уровни привилегий процессора цо 0, а пользователю - кольцо 3. 80286 Мы уже говорили о том, что необходимо ограничить использование некоторых команд процессора 80286. В табл. 5.2 показаны привилегированные команды (часть из них обсуждается далее). В первой части табл. 5.2 приведены команды, которые могут выполняться только в кольце 0, а выполнение их в любом другом кольце вызывает особый случай защиты. В эту группу команд не включены команды ввода-вывода, так как необязательно ограничивать их использование ядром безопасности. Процессор 80286 в регистре флажков (см. гл. 2) имеет поле уровня привилегий ввода-вывода IOPL, определяющее наименее привилегированное кольцо, в котором разрешены команды ввода-вывода. Если любая из этих команд (см. вторую часть табл. 5.2) выполняется в кольце, менее привилегированном, чем IOPL, возникает особый случай защиты. Как мы только что сказали, флажки, а именно поле IOPL, содержат важную информацию. Еще одним столь же важным элементом является флажок разрешения прерываний IF. Хотя обычным пользовательским программам нельзя изменить эти поля, команды загрузки флажков Таблица 5.2. Привилегированные команды Уровни команд Мнемоники команд Команды, недопустимые выше уровня 0 LIDT, LLDT, LGDT, LTR, LMSW, CLTS, HLT Команды, недопустимые выше INS, IN, OUTS, OUT, STI, CLI, LOCK ввода-вывода Команды, модифицируемые IRET, POPF в соответствии с уровнем 189
имеют и допустимые применения и не должны запрещаться для менее привилегированных колец. Поэтому, если команда загрузки флажков (а это команды IRET и POPF) выполняется в кольце, отличающемся от нулевого, поле IOPL остается неизменным; если такая команда выполняется в кольце, менее привилегированном, чем определяется IOPL, флажок IF также не изменяется. В любой из этих ситуаций особый случай не возникает. Вызов через кольца. Напомним одно из требований защиты: невозможность получить привилегии операционной системы иначе, как входя в нее в допустимой точке входа. Как стандартный способ запроса обслуживания операционной системы во многих компьютерах применяется специальная команда "вызов супервизора". В процессоре 80286 это реализуется обычным межсегментным вызовом процедуры в более защищенном кольце. Разработчики процессора поставили цель, чтобы все межсегментные вызовы действовали для вызывающей и вызываемой программ одинаково, независимо от того, переходит вызов из одного кольца в другое или нет. При этом (отдельно компилированную) подпрограмму не нужно изменять при перемещении ее из одного кольца в другое. Кроме того, компиляторы могут не обрабатывать вызовы между кольцами как специальный случай. Таким образом, программы на языках высокого уровня могут прямо вызывать операционную систему. Как будет показано далее, эта цель в процессоре 80286 была достигнута не полностью. На первый взгляд кажется, что ради поддержки защиты процессор 80286 должен запрещать менее привилегированным процедурам вызывать более привилегированные. Именно поэтому он предотвращает выполнение пользовательской программой тех операций, которые должны выполняться только операционной системой. Но при этом пользовательской программе запрещались бы и допустимые обслуживания, которые нужны ей от операционной системы. Поэтому процессор разрешает менее привилегированной процедуре вызывать более привилегированную, но ограничивает доступ разрещенными точками входа. Следовательно, пользовательская программа получает от операционной системы только те обслуживания, которые системе разрешено оказывать пользовательской программе. Допустимые точки входа идентифицируются в процессоре 80286 специальными дескрипторами, называемыми шлюзами вызова. Вызывающая программа выполняет косвенную передачу управления через шлюз вызова; команда CALL в вызывающей программе обращается к шлюзу вызова, а он определяет точку входа в вызываемой программе. Теперь для доступа пользовательской программы к конкретной процедуре операционной системы нужно обеспечить шлюз вызова (привилегия которого
го достаточно низка для доступа пользовательской программе) и заставить этот шлюз вызова обратиться в точку входа сильно защищенной операционной системы. Дескриптор шлюза вызова содержит виртуальный адрес нужной точки входа в более привилегированном сегменте. Команда вызова содержит виртуальный адрес, селектор которого адресует шлюз вызова; смещение здесь игнорируется. Одно из преимуществ хранения адреса точки входа в шлюзе вызова,а не в команде вызова, заключается в том, что процедуру можно изменять и рекомпилировать, не требуя рекомпилирования никаких программ, вызывающих ее через шлюз. Чтобы учесть любые изменения в точке входа процедуры, нужно изменить только виртуальный адрес в шлюзе вызова. Следовательно, старые программы можно выполнять с новой версией операционной системы, если позиции в GDT всех шлюзов для обслуживания старой операционной системы остались неизменными. Формат дескриптора шлюза вызова показан на рис. 5.12. Бит присутствия Р имеет такой же смысл, как и в дескрипторе сегмента. Поле DPL определяет, какая привилегия необходима для доступа к шлюзу, т.е. шлюзом можно пользоваться только при CPL < DPL. Селектор и смещение назначения определяют нужную точку входа. Назначением должен быть сегмент кода; шлюзы в шлюзы запрещены. Поле RPL селектора назначения игнорируется. Пятибитное поле счетчика слов показывает число параметров-слов, передаваемых при вызове. Для чего предназначено это поле и что делать, если параметров больше 2s - 1 = 31, мы поясним далее. 16 бит 8 бит 3 бита 5 бит 16 бит 16 бит 0 Доступ Селектор назначения Смещение назначения 7 5-6 0-4 б) Рис. 5.12. Формат шлюза вызова: в - формат дескриптора шлюза вызова, б - байт доступа в дескрипторе шлюза вызова Использование шлюза вызова показано на рис. 5.13. Здесь шлюз вызова имеет DPL = 2 и обращается к сегменту кода с DPL « 0. Шлюз вызова можно использовать как назначение для вызовов, осуществляемых в кольцах 0,1 и 2; но шлюз нельзя использовать из кольца 3. Кроме того, сегмент кода может прямо вызываться любой программой в кольце 0. 191
Чтобы проанализировать механизм вызовов и возвратов в виртуальном режиме, рассмотрим три случай: 1) вызывающая и вызываемая программы имеют одинаковые привилегии; 2) вызывающая программа менее привилегирована, чем вызываемая; 3) вызывающая программа более привилегирована, чем вызываемая. Если вызывающий и вызываемый сегменты имеют одинаковые привилегии, вызовы и возвраты действуют так же, как в реальном режиме (см. гл. 3). Это остается справедливым, даже если вызов проходит через шлюз: он просто перенаправляет назначение вызова. Содержимое стека после вызова показано на рис. 5.14,а; оно такое же, как в гл. 3. Если вызывающий сегмент менее привилегирован, чем вызываемый, вызов должен проходить через шлюз. Кроме того, вызываемая процедура должна использовать другой, более привилегированный сегмент стека, чем вызывающая процедура. Это объясняется двумя причинами. Во-первых, вызываемая процедура должна защищаться от возможного переполнения стека, которое возникает, если вызывающая процедура распределяет недостаточное стековое пространство. Именно по этой причине большинство операционных систем почти во всех компьютерах при вызове переключаются на свои стеки. Процессор 80286 в межкольцевых вызовах переключает стек автоматически. Вторая причина переключения стека оказывается более тонкой. Процессор 80286 разрешает задачам разделять сегменты. Возможно, что менее привилегированный сегмент стека менее привилегированной вызывающей процедуры разделяется с другой задачей. Менее привилеги- 192
Увеличение адресов памяти Параметрь Старый CS р— Старый IP Направление роста стека а) SS : SP из TSS Старый SS Старый SP Параметры Старый CS р—► Старый IP I Направление I роста I стека Стек вызывающей программы Стек вызываемой программы Рис. 5.14. Содержимое стека после длинного вызова: a — внутри кольца, б - между кольцами рованная программа, выполняющаяся в этой другой задаче, может разрушить сегмент стека, когда он используется более привилегированной вызванной процедурой в первой задаче. В общем, привилегированные процедуры должны считать менее привилегированные записываемые сегменты подвергающимися разрушению. Процессор 80286 получает новые значения регистров SS и SP для нового стека из специального сегмента, называемого сегментом состояния задачи TSS и ассоциируемого с задачей. Каждая задача имеет свой сегмент состояния задачи. Подробнее мы поговорим об этих сегментах в следующем разделе. Пока нам нужно только знать, что каждый TSS содержит три потенциальных пары значений для регистров SS и SP: по одной для каждого из колец 0,1 и 2. Пара для кольца 3 не нужна, так как не существует вызова, в котором вызывающая процедура менее привилегирована, чем вызываемая процедура в кольце 3. После загрузки регистров SS и SP для адресации нового стека процессор включает в него старые значения из регистров SS и SP (т.е. те, которые адресуют вершину старого стека). Затем он копирует все параметры из старого стека в новый. Поле счетчика из шлюза вызова определяет, сколько слов копировать. Наконец, процессор включает в новый стек старые значения регистров CS и IP, т. е. адрес возврата. Содержимое стека после вызова показано на рис. 5.14,6. 7 Заказ 6126 193
Процессор мог бы не копировать параметры в новый стек; вызванная процедура могла обращаться к ним из стека вызывающей процедуры. Однако при этом вновь возникает проблема разрушения. Кроме того, это потребовало бы от вызывающей процедуры учета переключения стека. Заставив процессор автоматически скопировать параметры, вызванная процедура может работать одинаково, независимо от того, вызвана она из своего или другого кольца. Из сравнения рисунков 5.14,с и б видно, что верхняя часть стека, видимая вызванной процедуре, одинакова в обоих случаях. Команда возврата может узнать о том, что она осуществляет возврат из межкольцевого вызова, сравнивая текущее значение CPL с уровнем CPL вызывающей программы. (Напомним, что уровень CPL сохраняется в поле RPL регистра CS, поэтому процессор может определить CPL вызывающей программы, анализируя значение CS, которое было включено в стек как часть адреса возврата.) При возврате из межкольцевого вызова команда возврата должна произвести следующие действия: 1. Извлечь адрес возврата и поместить его в регистры CS и IP. 2. Извлечь параметры из стека вызываемой программы. 3. Переключиться на стек вызывающей программы, извлекая старые значения SS и SP из стека и помещая в регистры SS и SP. 4. Извлечь параметры из стека вызывающей программы. 5. Продолжить выполнение вызывающей программы. Кроме того, команда возврата сбросит содержимое регистров DS или ES, если любой из них адресует сегмент, более привилегированный, чем вызывающая программа. Это действие запрещает вызывающей программе недопустимый доступ к привилегированным сегментам, случайно оставленным вызванной процедурой. В общем, этот механизм вызова/возврата удачно скрывает различие между внутрикольцевым и межкольцевым вызовами во всех случаях, кроме следующих двух маловероятных ситуаций: 1. При передаче переменного числа параметров или более 31 парамет* pa-слова, вызванная процедура должна обнаружить, вызвана ли она из другого кольца, и при утвердительном ответе явно скопировать параметры из старого стека в новый. 2. Бели вызванная процедура формирует индикатор (см. описание команд ENTER и LEAVE в гл. 3), то в случае межкольцевого вызова индикатор должен относиться к старому стеку. Первой ситуации легко избежать, обеспечив использование менее 32 параметров-слов каждой процедурой операционной системы, вызываемой из менее привилегированного кольца. Второй ситуации можно избежать, если разместить все такие процедуры на самом внешнем уровне статического вложения процедур или написать процедуры опера- 194
ционной системы на языках (например, языке Си), не использующих индикатор. Наконец, рассмотрим случай, когда вызывающая программа более привилегирована, чем вызываемая. В этом маловероятном случае операционная система запрашивает обслуживание от пользовательской программы, что фактически запрещается процессором 80286. Чтобы объяснить причину этого, нам придется подробнее разобраться с командой возврата, а не вызова. Здесь команда передает управление из менее привилегированного кольца в более привилегированное. Предположим, что до выполнения команды возврата "злонамеренная" программа формирует адрес возврата (значения для регистров CS и IP) и включает его в стек, т. е. осуществляет "подделку" адреса возврата. Далее предположим, что этот поддельный адрес возврата соответствует защищенной точке входа в операционной системе. Когда выполняется команда возврата, пользовательская программа войдет в операционную систему в недопустимой точке входа. Следовательно, если разрешить вызовы (и возвраты) менее привилегированных колец, "злонамеренный" пользователь может "ворваться" в операционную систему. По этой причине подобные вызовы и возвраты приводят к особому случаю защиты. Чтобы обеспечить безопасность при возврате из менее привилегированной процедуры в более привилегированную, в процессоре 80286 мог бы быть предусмотрен более сложный механизм вызова/возврата. Вместо разработки такого механизма (ведь и встроенный механизм довольно сложен) была предусмотрена регистрация особого случая. Если же такой вызов абсолютно необходим, то можно написать специальную процедуру, программно моделирующую вызов, в кольце 0. Мы видели, как при вызове защищенной процедуры используется шлюз вызова. Процессор 80286 при определенных обстоятельствах позволяет программе осуществить переход к шлюзу вызова. Чтобы показать опасность такого перехода, предположим, что программа включает в стек поддельный адрес перехода. Затем программа может осуществить межкольцевой переход через шлюз вызова к более привилегированной процедуре, которая ожидает вызова, а не перехода. Переход оказывается замаскированным под вызов. Когда в конце концов привилегированная процедура выполнит свою команду возврата, выполнение будет продолжено с ячейки, определяемой поддельным адресом возврата, а им может быть недопустимая точка входа в более привилегированное кольцо. Следовательно, защита не срабатывает. По этой причине межсегментные переходы разрешены только внутри кольца. Переходы к назначениям в другом кольце приводят к особому случаю защиты. Переход к шлюзу вызова разрешен, если только назначение шлюза вызова находится в том же кольце, что и команда перехода. 195
Мы рассмотрели два способа нарушения безопасности, если бы процессор 80286 не запрещал некоторые виды команд. Выявление таких лазеек при разработке процессора оказывается сложной задачей. Первоначально межкольцевые переходы были встроены в процессор, а затем было обнаружено, насколько они опасны. Таким образом, процедуре запрещено вызывать менее привилегированную процедуру. Процедура может вызывать более привилегированную, но вызов должен осуществляться через шлюз. Если процедура вызывает процедуру с одним и тем же уровнем привилегий, шлюз не обязателен. Переходы разрешены только между сегментами с одинаковыми привилегиями; здесь шлюз также не обязателен. Атака троянского коня. Рассмотрим процедуру операционной системы из кольца 1, которая записывает текущие дату и время в цепочку в ячейке, определяемой единственным параметром процедуры - виртуальным адресом. Злонамеренная программа в кольце 3 вызывает эту процедуру, передавая как параметр виртуальный адрес, содержащий селектор для сегмента в кольце 2. После этого процедура кольца 1 записывает дату и время в указанную ячейку, разрушая сегмент кольца 2. Злонамеренная программа использовала параметр в качестве троянского коня, который разрушил сегмент кольца 2 после того, как параметр передан в кольцо 1 "доверчивой" процедуре даты и времени. Чтобы отразить эту атаку, используется поле RPL в селекторах и команда ARPL (скорректировать RPL). По существу, поле RPL искусственно увеличивает текущий номер кольца (уменьшает привилегии), который действует при использовании селектора. Увеличенный номер кольца равен max (CPL, RPL). Следовательно, мы можем с помощью поля RPL отметить селектор степенью нашего доверия к нему. Вызванная процедура должна содержать команды ARPL, которые устанавливают поле RPL всех селекторных параметров на уровень привилегий вызывающей программы. Этим вызванная процедура может защититься от своеобразной атаки троянского коня. Такая ситуация показана на рис. 5.15. Несколько более лучший подход заключается в том, чтобы установить поле RPL на максимальное значение (меньшие привилегии) старого RPL и уровня привилегий вызывающей программы. Здесь поле RPL селектора не уменьшается (не становится более привилегированным), еслг он передается по цепи предусмотрительных процедур с увеличива- ющик/ися привилегиями, каждая из которых устанавливает RPL, а затем вызывает следующую процедуру. Команда ARPL имеет два операнда: первый - операнд-слово (память или регистр), а второй - регистровый операнд-слово. Предполагается, что оба операнда являются селекторами. Поле RPL первого операнда устанавливается на максимальное значение обоих RPL. Если команда ARPL 196
Селектор Селектор Селектор программа программа программа Селектор троянского коня. передается в кольцо... но отражается установкой RPL Рис. 5.15. Отражение атаки троянского коня увеличивает поле RPL первого операнда, то она устанавливает в 1 флажок нуля. В противном случае команда ARPL сбрасывает флажок нуля. Следовательно, предусмотрительная процедура может загрузить селектор из своего адреса возврата в регистр, а затем применить команду ARPL к каждому селекторному параметру, чтобы защититься от атаки. Программисты, не заботящиеся о троянских конях, могут привести два успокаивающих объяснения: 1. Если программа использует RPL = 0 во всех своих селекторах, то ее можно применять в любом кольце без опасности ложных особых случаев защиты. Это объясняется критерием допустимого доступа 2, Программа, работающая только в кольце 3 (пользовательском кольце), дает одни и те же результаты, независимо от используемых ею значений RPL. Объясняется это тем, что критерий допустимого доступа приобретает вид: Наконец, для предусмотрительных программ введены две команды, которые проверяют, доступен ли сегмент без риска возникновения особого случая защиты. Команда VERR (проверить на считывание) воспринимает операнд-слово (память или регистр), который считывается селектором. Если селектор можно безопасно использовать для считывания, команда VERR устанавливает флажок нуля в 1; в противном случае флажок нуля сбрасывается. Команда VERW (проверить на запись) аналогична команде VERR, но она проверяет разрешение записи. Подчиненные сегменты. Недостаток в использовании команды ARPL заключается в том, что она должна явно фигурировать в программе. Если DPL > max (CPL, RPL) = max (CPL, 0) = CPL. DPL > max (CPL, RPL) = max (3, RPL) = 3. 197
процедура никогда не требует больше привилегий, чем вызывающая ее программа, процессор 80286 обеспечивает автоматическую коррекцию ее привилегий. Например, в системе может быть процедура для преобразования двоичных целых чисел в символьный код ASCII, которая используется в каждом кольце. Поскольку вызовы менее привилегированных процедур запрещены, эта процедура должна находиться в кольце 0, чтобы ее можно было вызвать из кольца 0. Но, в общем случае, процедура преобразования требует достаточно привилегий только для доступа к своим аргументам и возвращению результата, т. е. только такие же привилегии, как и вызывающая ее программа. В процессоре 80286 для таких ситуаций предусмотрен элегантный механизм. Напомним, что в дескрипторах исполняемых сегментов (и только для таких сегментов) имеется бит подчинения С. Если этот бит установлен в 1, то обычные правила относительно этой CPL и DPL не действуют, а вместо них вводятся другие. Процедуры в подчиненном сегменте можно вызывать или переходить к ним прямо из любой программы, не более привилегированной, чем подчиненный сегмент: поле CPL вызывающей программы должно быть больше или равно полю DPL подчиненного сегмента. Шлюз вызова не требуется, и стеки не переключаются. Когда производится передача управления подчиненному сегменту, поле CPL не изменяется; оно остается на уровне вызывающей программы. Следовательно, подчиненный сегмент автоматически "перемещается" или "подчиняется" кольцу вызывающей программы. Это правило действует даже в том случае, если подчиненный сегмент вызывается через шлюз. Сегменты кода иногда содержат константы, используемые программой и, следовательно, считываемые при ее выполнении. Обычно, если сегмент кода считываемый, то он считывается только из содержащего его кольца и из колец с меньшими номерами (т. е. более привилегированных). Чтобы считываемый подчиненный сегмент мог обращаться к своим встроенным константам, считываемые подчиненные сегменты могут считываться из любого кольца. 5.4. МУЛЬТИЗАДАЧНОСТЬ Процессор 80286 поддерживает несколько задач путем переключения с одной задачи на другую. Для этого он ассоциирует с каждой задачей сегмент памяти, содержащий всю информацию, необходимую для запуска и останова задачи. Этот специальный сегмент называется сегментом состояния задачи TSS. Кроме того, в процессоре имеется 16-битный регистр задачи, содержащий селектор GDT для сегмента TSS текущей выполняющейся задачи. Для ускорения доступа к выбранному TSS с 198
регистром задачи ассоциирован теневой регистр, содержащий выбранный' дескриптор. Хотя регистр задачи можно загружать и запоминать с помощью команд LTR (загрузить в регистр задачи) и STR (запомнить содержимое регистра задачи), обычно процессор оперирует регистром задачи автоматически в ходе переключения задач. Основное назначение сегмента состояния задачи - сохранять содержимое регистров задачи, когда процессор ее не выполняет. При переключении на другую задачу процессор 80286 предпринимает (примерно) следующие действия: 1) сохраняет все "видимые" программе регистры (за исключением регистра GDT) в сегменте состояния задачи, на который показывает регистр задачи; 2) загружает в регистр задачи селектор для нового сегмента состояния задачи; 3) загружает свои регистры из нового сегмента состояния задачи и продолжает выполнение новой задачи. Сегменты состояния задач, как и сегменты LDT, имеют свои специальные дескрипторы. В регистр задачи можно загружать только селекторы дескрипторов TSS. Формат дескриптора сегмента состояния задачи показан на рис. 5.16. Нам уже знакомы все его поля за исключением бита занятости В. Процессор 80286 устанавливает в 1 бит занятости в дескрипторе TSS для текущей выполняющейся задачи и сбрасывает его в 0, когда переключается на другую задачу. Бит занятости используется в системах с несколькими процессорами 80286, чтобы предотвратить выполнение одной и той же задачи двумя процессорами одновременно. Переключение на задачу с установленным битом занятости вызывает особый случай защиты. 16 бит 8бит 24 бита 16 бит Доступ База Предел а) 5-6 1 -4 DPL О 0 1 i i б) Рис. 5.16. Формах дескриптора сегмента состояния задачи: а — дескриптор состояния задачи, б — его байт доступа Формат первых 44 байт сегмента состояния задачи показан на рис. 5.17. При необходимости сегмент состояния задачи можно расширить для дополнительной информации, требуемой программами операционной системы (владелец задачи, учетная информация и т. п.). 199
16 бит Обратная связь- Смещение РиС# 5.17. Формат аппаратно-определенной части сегмента п состояния задачи Начало стека кольца О Начало стека кольца 1 Начало стека кольца 2 IP Флажки АХ сх DX вх SP BP si DI ES CS SS DS Регистр LDT 10 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 Первое поле TSS представляет собой поле обратной связи; о нем мы поговорим позже. Затем следуют виртуальные адреса трех стеков по одному из трех колец защиты 0-2. Каждый адрес определяет низ стека (т.е. наибольший адрес). Использование этих стеков в межкольцевых вызовах было описано в разд. 5.3. Остальная аппаратно-распознаваемая часть сегмента состояния задачи применяется для сохранения регистров. Регистры, которые не показаны на рис. 5.17 (регистр GDT, регистр задачи, теневые регистры и др.), не запоминаются при переключении задач: они либо остаются неизменными, либо определяются вновь. Одна из причин быстрого переключения задач (22мкс при частоте синхронизации 8 МГц) заключается в том, что объем информации, подлежащий сохранению или повторному определению, сравнительно невелик. Селектор для дескриптора TSS нельзя использовать для считывания или записи в сегмент состояния задачи. Он предназначен только для переключения задач. Специальной команды для переключения задач в процессоре 80286 нет. Вместо этого команда межсегментного перехода (JMP), которая адресует сегмент TSS, вызывает не переход, а переключение на соответствующую задачу. В этом случае смещение в адресе перехода команды JMP игнорируется. Такой прием аналогичен рассмотренному в разд. 5.3, когда команда вызова действует по-разному в зависимости от того, адресует она сегмент кода или шлюз. Разработчики процессора 80286 долго мучились над тем, чтобы одна и та же команда вызова в программе работала правильно, независимо от того, переходит она к сегментам кода или через шлюзы. Переключение задач настолько отличается от перехода, что невозможно представить себе подстановку "перехода к сег- 200
менту состояние задачи" вместо "перехода к сегменту кода" (или наоборот) и заставить программу все же сделать что-то осмысленное. Функции команды перехода и команды переключения задач были объединены в один код операции просто для экономии кодов операций. Обычные правила защиты сегментов применимы и к TSS. Сегмент состояния задачи можно использовать только внутри кольца, определяемого его уровнем DPL, или кольцом с меньшим номером (более привилегированным). Однако шлюз задачи позволяет программе переключиться на задачу, соответствующую сегменту TSS в кольце с меньшим номером. Формат шлюза задачи показан на рис. 5.18. 16 бит 8 бит I О I Доступ а) 7 5-6 0-4 Рис. 5.18. Формат шлюза задачи: а — шлюз задачи, б — его байт доступа Шлюзы задач, в отличие от шлюзов вызовов, не накладывают ограничений на точку входа, но их все же можно использовать для обеспечиния защиты. Предположим, например, что конкретная задача выполняет обслуживание, которое мы хотим ограничить выбранными пользовательскими задачами. Можно разместить сегмент TSS для задачи обслуживания в таблице GDT кольца 0. Задачам, которым разрешено пользоваться обслуживанием, можно задать шлюзы задачи обслуживания в их таблицах LDT. Тогда задачи без шлюза получить обслуживание не смогут. Пример: разделение времени. Рассмотрим, как команда переключения задач позволяет реализовать простой вариант разделения времени. Будем полагать, что все сегменты TSS содержат в их необязательных частях 4-байтное программное поле связи. Это позволяет нам связать сегменты состояний задач в списки, размещая в полях связи виртуального адреса (фактически используется только селектор виртуального адреса). На рис. 5.19 показан список задач, ожидающих времени процессора 80286. Регистр задачи адресует сегмент TSS той задачи, которая сейчас выполняется. Поле связи в каждом TSS адресует сегмент состояния следующей задачи, а поле связи последнего TSS показывает на первый. 8 бит 16 бит Селектор TSS 16 бит = Игнорируются 201
регистр задачи|-—► Сегмент Сегмент Сегмент состояния состояния состояния задачи задачи задачи Связь- Связь—j- Связь— Рис. 5.19. Список задач, готовых к выполнению Так как дескрипторы сегментов TSS нельзя использовать для обращений по записи и считыванию, мы предположим, что сразу за каждым дескриптором TSS находится дескриптор сегмента данных для того же самого сегмента. Такая форма альтернативного именования относительно проста, так как дескриптор можно проверить на альтернативные имена, просто контролируя дескрипторы, которые в дескрипторной таблице находятся сразу перед ним и после него. Мы можем получить селектор альтернативного дескриптора сегмента данных, прибавляя 8 к селектору для дескриптора TSS (напомним, что младшие три бита селектора содержат поля RPL и TI). Приводимая ниже процедура перемещает текущую выполняющуюся задачу в конец списка ожидания, а затем переключается на следующую задачу в списке. Такая стратегия планирования называется круговой,. Ассемблерные директивы, для определения полей в сегменте состояния задачи (в процедуре ок называется TSS) опущены, но остальные директивы включены dispatch PROC FAR PUSHF PUSH DS PUSH AX ; Сообщить Ассемблеру о процедуре ; Сохранить флажки в стеке ; Сохранить DS в стеке ; Сохранить АХ в стеке STR АХ ; Передать в АХ регистр задачи ADD АХ,3 ; Изменить селектор для TSS MOV DS,AX ; Теперь можно считать TSS ASSUME DS:TSS ; Сообщить Ассемблеру об испольэов-<нми TSS ; J MP DWORD PTR link 35 Переключиться на следующую эвда>-у Продолжить отсюда,когда управление возвращается этой задаче POP POP POPF АХ DS Восстановить АХ Восстановить DS Восстановить флажки 202
RET dispatch ENDP ; Возврат Рассмотрим, что происходит, когда программа вызывает процедуру dispatch. После сохранения содержимого некоторых регистров и получения доступа по считыванию из текущего сегмента TSS; процедура dispatch выполняет косвенный переход JMP (фраза DWORD PTR заставляет ассемблер образовать косвенный межсегментный переход) через виртуальный адрес, находящийся в поле связи текущего сегмента состояния задачи. Так как этот виртуальный адрес показывает на TSS, команда JMP осуществляет переключение задачи, а не переход. Это вызывает приостановку текущей задачи и выполнение следующей. Указатель команды приостановленной задачи, запомненной в ее TSS, будет показывать на команду POP, находящуюся после команды JMP. Если каждая из остальных задач в списке также вызывает процедуру dispatch в ходе своего выполнения, то через некоторое время приостановленная задача окажется в начале списка и будет возобновлена. Выполнение продолжается с команды POP, и процедура dispatch осуществляет возврат. Таким образом, вызов процедуры dispatch не оказывает побочных воздействий на вызывающую ее задачу, но дает другим задачам шанс на выполнение. К сожалению, мы не можем рассчитывать на то, что задачи достаточно "внимательны", чтобы вызывать процедуру dispatch через регулярные интервалы времени. Вместо этого можно использовать интервальный таймер (например, 8254) для генерирования сигнала прерывания процессора 80286, например через каждую тысячную долю секунды. Если превратить dispatch в процедуру прерывания (для этого нужно удалить команды PUSHF и POPF, а также заменить команду RET на команду IRET) и ассоциировать ее с прерыванием таймера, то она будет автоматически вызываться через каждую тысячную долю секунды. Таким образом, каждая задача получает квант времени (почти) в одну тысячную долю секунды и разделение задач во времени реализовано. Переключение численных задач. Одна из причин очень быстрого переключения задач в процессоре 80286 заключается в том, что при этом должны сохраняться и восстанавливаться всего 30 байт регистров. Однако команда переключения задач этого процессора не учитывает наличия сопроцессора 80287. Состояние всех регистров сопроцессора занимает 94 байта. Сохранение и восстановление этой дополнительной информации может существенно замедлить переключение задач. Проще всего учесть сопроцессор 80287, если распределить 94 байта в необязательной части каждого сегмента состояния задачи и модифицировать процедуру dispatch, добавив команду FSAVE до и команду FRSTOR после команды переключения задач. Но при этом время на 203 ; Сообщить Ассемблеру о конце процедуры
сохранение и восстановление содержимого регистров сопроцессора впустую расходуется в тех задачах, которые не используют сопроцессор. По-видимому, большинство задач в системе окажутся такими "нечисленными" задачами. Было бы хорошо, если бы операционная система могла избежать сохранения и восстановления содержимого регистров сопроцессора 80287 за исключением только тех случаев, в которых это необходимо. Хитрость состоит в том, чтобы запомнить последнюю задачу, которая выполняла команду сопроцессора 80287, и задержать сохранение содержимого его регистров для этой задачи до тех пор, пока другая задача не будет выполнять команду сопроцессора. Для реализации этого в процессоре 80286 предусмотрен регистр, называемый словом состояния машины MSW; его нет в микропроцессоре 8086. (Не расстраивайтесь, остался еще только один регистр процессора 80286, который мы не рассмотрели.) Слово состояния машины можно загружать и запоминать с помощью команд LMSW (загрузить слово состояния машины) и SMSW (запомнить слово состояния машины). Формат слова состояния машины показан на рис. 5.20; в нем используются только четыре бита, а остальные зарезервированы для будущего расширения. 4-15 3 2 10 Рис. 5.20. Слово состояния машины Если к процессору 80286 подключен сопроцессор, операционная система во время инициализации должна сбросить в нуль бит режима эмуляции ЕМ в слове состояния машины. Если бит ЕМ равен 1, то процессор 80286 при встрече команды ESC генерирует особый случай отсутствия сопроцессора. (Напомним, что все команды сопроцессора 80287 являются вариантами команды ESC.) Поэтому при ЕМ ■ 1 процедура обработки особого случая отсутствия сопроцессора может эмулировать команды сопроцессора программно. Далее, если сопроцессор 80287 эмулируется программно, его моделируемые регистры должны храниться в памяти как часть логического адресного пространства задачи. Это делает ненужным сохранение и восстановление моделируемых регистров при переключениях задач. Бит задача переключена TS слова состояния машины автоматически устанавливается в 1, когда процессор 80286 осуществляет переключение задач. Он остается установленным в 1 до явного сброса командой CLTS (сбросить бит задача переключена), не имеющей операндов. Если процессор 80286 встречает команду ESC и обнаруживает TS - 1, то он генерирует особый случай отсутствия сопроцессора независимо от состояния бита 204
ЕМ. Следовательно, процедура обработки особого случая отсутствия сопроцессора должна вначале проверить бит ЕМ. Если он находится в состоянии 1, необходимо эмулировать команду. Если же ЕМ = 0, предпринимаются следующие действия: 1. Сбросить бит TS в 0. 2. Проверить, является ли текущая задача последней задачей, вызвавшей этот особый случай. Если это не так, осуществить возврат в программу. Если нет, необходимо сохранить содержимое регистров сопроцессора в TSS последней задачи, вызвавшей особый случай. 3. Восстановить содержимое регистров сопроцессора из текущего TSS. 4. Отметить тот факт, что текущая задача стала последней задачей, вызвавшей этот особый случай. 5. Осуществить возврат в программу. После возврата в программу на шаге 2 или 5 процессоры 80286 и 80287 продолжают выполнение с команды ESC, вызвавшей особый случай. Поскольку бит TS теперь сброшен, эта команда выполняется, не вызывая еще одного особого случая отсутствия сопроцессора. Если сопроцессор подключен к процессору, то во время инициализации операционная система должна установить в 1 бит присутствия сопроцессора MP в слове состояния машины. Тогда процессор 80286 будет учитывать команды WAIT, а также команды ESC, когда бит TS содержит 1. Микропроцессор 8086 не имеет особого случая отсутствия сопроцессора и не может переключать задачи, поэтому для совместимости с ним в реальном режиме биты TS, MP и ЕМ должны быть сброшены в нуль. Поскольку в реальном режиме переключения задач быть не может, бит TS будет оставаться в состоянии 0 и особый случай отсутствия сопроцессора никогда не возникнет. Использование бит MP и ЕМ показано в табл. 5.3. Таблица 5.3. Состояния бит ЕМ и MP Биты Реакция системы ЕМ MP 0 1 Сопроцессор 80287 имеется 1 0 Сопроцессора 80287 нет 0 0 Совместимость с 8086 1 1 Запрещенная комбинация Последний бит в слове состояния машины - это бит защита разрешена РЕ. Он установлен в состояние 1, если процессор 80286 работает в виртуальном режиме, и в состояние 0 при работе в реальном режиме. О назначении этого бита мы поговорим в разд. 5.6. 205
Вызов задачи. Многие операционные системы поддерживают такое взаимодействие с пользователем, которое можно проиллюстрировать на следующем примере. Пользователь отлаживает свою программу с помощью интерактивного отладчика. Он обнаруживает, что должен модифицировать входной файл, поэтому он дает команду отладчику, которая приостанавливает отладчик и инициирует новую задачу, содержащую интерактивный редактор текста. Работая с редактором текста, пользователь решает уничтожить файл и дает команду редактору текста инициировать программу удаления файла. Редактор текста инициирует новую задачу, содержащую программу удаления файла, и приостанавливает себя. Когда программа удаления файла закончена, редактор текста возобновляется в той точке, где пользователь запрашивал удаление файла. Когда пользователь, наконец, завершает редактирование файла, он заканчивает редактор текста, который переводит его в ту точку отладчика, из которой он вышел. После этого пользователь продолжает отладку. Отметим, что в любой момент времени выполняется только одна задача, а дисциплина переключения задач напоминает стек. Процессор 80286 поддерживает рассмотренный механизм аппаратно. Аналогично тому, как команде JMP разрешено обращаться к сегменту состояния задачи, так и команде вызова CALL разрешено обращаться к сегменту состояния задачи или шлюзу задачи. Команда CALL, адресующая сегмент состояния задачи или шлюз задачи, вызывает переключение на выбранную задачу, как и команда JMP. Но, кроме того, процессор 80286 помещает селектор вызывающей задачи (старое содержимое регистра задачи) в поле обратной связи сегмента состояния вызываемой задачи. Он также устанавливает в 1 флажок вложенной задачи NT в вызываемой задаче. На рис. 5.21 показано, что происходит, когда задача А (инициированная командой JMP) вызывает задачу В, вызывающую задачу С, а эта задача в свою очередь вызывает задачу D. Текущая задача — О Регистр задачи]—! TSS.A TSS В TSS С TSSD —Обратная —Обратная —Обратная связь— связь— —связь— NT^.0 NT-1 NT = 1 NT-1 Занят Занят Занят Занят 206 Рис. 5.21. Цепь, образованная тремя вызовами задач
\ Команда JMP перехода к задаче сбрасывает флажок NT в нуль, поэтому он сообщает, инициирована ли задача командами JMP или CALL. Ещё одно различие между командами JMP и CALL заключается в интерпретации бита занятости В в дескрипторе TSS. Напомним, что у выполняющейся программы бит В установлен в 1, поэтому попытка второго процессора 80286 одновременно выполнить эту же задачу вызывает особый случай. Команда JMP сбрасывает в 0 бит В в "покидаемой" задаче и устанавливает в 1 бит В в инициируемой задаче. С другой стороны, команда CALL не модифицирует бит В вызывающей задачи, но устанавливает в 1 бит В в вызываемой задаче. Следовательно, у каждой задачи в цепи вызовов бит В будет установлен в 1 (см. рис 5.21). Такое действие эквивалентно запрещению реентрантных и рекурсивных вызовов задач. Команда IRET в задаче с битом NT, содержащим 1, производит переключение на задачу, адресуемую полем обратной связи текущего сегмента TSS. Если NT - 0, то команда IRET действует так, как было описано в гл. 3. Одна из причин использования команды IRET, а не команды RET для возврата из вызванной задачи, заключается в том, чтобы внутри вызванной задачи пользоваться обычными процедурами. Процессор 80286 автоматически сбрасывает флажок NT на время работы любых процедур прерываний, поэтому они также действуют правильно. Вторая причина применения команды IRET для возврата из задачи состоит в том, что наиболее важное использование вызовов задач связано с обработкой прерываний. Мы получаем много преимуществ, реагируя на прерывание вызовом задачи, а не вызовом процедуры. Такие вызываемые задачи называются задачами прерываний, и мы подробно обсудим их в следующем разделе. Если мы вызовем процедуру, разрешим ей осуществить возврат, а затем вызовем ее еще раз, то, очевидно, вход в процедуру произойдет в одном и том же месте в обоих вызовах. Если же мы вызовем задачу, разрешим ей осуществить возврат, а затем вызовем еще раз, то вход осуществится повторно на команде, находящейся за командой IRET, которая производила возврат. По этой причине задачи, которые рассчитаны на вызов, обычно имеют следующую форму: start: 1 Выполнить обслуживание IRET JMP, start 207
5.5. прерывания и особыв случаи Программа процессора 80286 может быть прервана по одной из урех причин: / 1. Подключенное к процессору периферийное устройство посылает сигнал прерывания. 2. Команда процессора 80286 (или 80287) вызывает особый случай. 3. Программа выполняет команду INT прерывания. Каждой конкретной причине прерывания назначен номер от 0 до 255. Некоторые номера прерываний уже назначены аппаратно процессором 80286 (например, для всех его особых случаев), другие присваиваются аппаратной системой, в которую встроен процессор (например, прерывания дискового накопителя), еще одни зарезервированы для дальнейшего расширения, а все остальные могут использоваться программами. Когда возникает прерывание, процессор 80286 использует номер прерывания как индекс в таблице. Из таблицы извлекается адрес обработчика прерывания; затем обработчик вызывается для производств?, конкретной обработки, требуемой прерыванием. Ситуация похожа 1та то, как будто в программе в точке прерывания вставлена команда вызова. Такова общая картина обработки прерываний процессором 80286 в реальном и виртуальных режимах. Однако в деталях эти два режима значительно различаются. Прерывания и особые случаи в реальном режиме. Мы уже обсуждали прерывания в реальном режиме в гл. 3, поэтому здесь мы лишь кратко коснемся их. Номер прерывания используется для индексирования таблицы с 4-байтными элементами. Таблица начинается по физическому адресу 0 и занимает 256 х 4 = 1024 байт. Каждый элемент таблицы представляет собой реальный адрес начала процедуры прерывания. Процедуры прерываний аналогичны обычным процедурам (без параметров) за исключением следующего: 1. Когда вызывается процедура прерывания, процессор 80286 до включения в стек адреса возврата включает в него флажки. 2. Перед входом в процедуру прерывания флажки трассировки TF и разрешения прерываний IF сбрасываются в нуль. 3. Для автоматического восстановления флажков процедура прерывания должна осуществлять возврат командой IRET, а не командой RET. В табл. 5.4 показаны зарезервированные номера прерываний для реального режима. Большинство из них уже обсуждалось в этой главе и в гл. 3. Среди нерассмотренных остались следующие: Особый случай ошибки деления. Возникает, если частное слишком большое или делитель равен нулю. В микропроцессоре 8086 адресом возврата процедуры прерывания для этого особого случая является 208
Таблица 5.4. Зарезервированные прерывания реального режима 17 31 Описание прерывания 0 \ Особый случай ошибки деления 1 < Прерывание пошаговой работы 2 Немаскируемое прерывание 3 Контрольный останов 4 Особый случай переполнения 5 Особый случай превышения диапазона 6 Особый случай недействительного кода операции 7 Особый случай отсутствия сопроцессора 8 Особый случай слишком малой ЮТ 9 Особый случай превышения сегмента сопроцессором 10 Зарезервировано И Зарезервировано 12 Зарезервировано 13 Особый случай превышения сегмента 14 Зарезервировано 15 Зарезервировано 16 Особый случай сопроцессора Зарезервированы адрес команды, находящейся после команды деления. В процессоре 80286 адреса возвратов для особых случаев адресуют первый байт "виноватой" команды. Все префиксы считаются частью команды. Мы увидим преимущества такого соглашения при обсуждении далее вопроса о возможностях повторного запуска ("рестартируемости"). Особый случай недействительного кода операции. Возникает, если встречается недействительный код операции или команда длиной более 10 байт. Единственный способ образовать допустимую команду такой длины - продублировать некоторые префиксы. Микропроцессор 8086 не имеет ограничений на длину команд; более того, его поведение непредсказуемо, когда встречается недействительный код операции. Особый случай слишком малой ЮТ. См. далее прерывания в виртуальном режиме. Особый случай превышения сегмента. Возникает, если операнд (в памяти) команды процессора 80286 не помещается в сегмент, например, если операнд-слово имеет смещение FFFF. В микропроцессоре 8086 такой операнд "закругляется" в сегменте на смещение 0. 209
Особый случай превышения сегмента сопроцессором. Аналогичен предыдущему особому случаю, но "виноватой" оказывается команда сопроцессора 80287. / Особый случай сопроцессора. Под ним понимается то, что происходит в процессоре 80286 при возникновении любого незамаскированного рсобо- го случая в сопроцессоре 80287. ( Приоритеты прерываний. Прерывания от некоторых периферийных устройств являются более срочными или важными, чем от других. Если ожидать одну миллисекунду до реагирования на прерывание от клавиатуры, человек не заметит разницы. Если же ожидать одну миллисекунду до реагирования на прерывание от дискового накопителя, диск к моменту реагирования повернется в новое положение; теперь придется подождать, пока диск не повернется в первоначальное положение, и попытаться отреагировать вновь. Если ожидать одну миллисекунду до реагирования на прерывание от прецизионного станка, он может повредить обрабатываемую деталь. Очевидно, потребуется как-то упорядочить прерывания в системе, назначив важным из них более высокий приоритет. В самом процессоре 80286 схем для этого нет, поэтому обычно все прерывающие устройства подключаются к микросхеме 8259А контроллера прерываний, а она подключается к процессору 80286. Контроллер прерываний 8259А имеет несколько режимов работы. В наиболее часто используемом режиме каждому устройству назначается уникальный приоритет. Если прерывания от двух устройств возникают одновременно, контроллер передает в процессор прерывание с более высоким приоритетом, а менее приоритетное прерывание запоминает. Как только процессор оканчивает обработку первого прерывания, контроллер сообщает ему о втором прерывании. Как же контроллер прерываний узнает о том, когда процессор заканчивает обработку прерывания? Для этого программист вставляет в конце процедуры прерывания каждого внешнего устройства специальные команды; эти команды выводят особый код конца прерывания в порт, подключенный к контроллеру прерываний. Примерный вид таких команд: MOV DX.inpartnum MOV AL.eoicode OUT DX,AL Номер порта inportnum и код конца прерывания eoicode зависят от особенностей конкретных схем и режима работы контроллера прерываний. А что произойдет, если прерывание от устройства возникает в середине процедуры прерывания? Если йовоё прерывание име?т приоритет, 210 ;
меньший или равный приоритету обслуживаемого прерывания, контроллер задержит новое прерывание до завершения процедуры прерывания. Есщ же новое прерывание имеет более высокий приоритет, чем обслуживаемое, то возможны два случая: IV Если прерывания разрешены (IF = 1), процедура прерывания будет сама прервана и вызывается процедура прерывания с более высоким приоритетом. Такая ситуация называется вложением прерываний и ее правильно обрабатывают и процессор, и контроллер прерываний. 2. Если прерывания запрещены (IF = 0), новое прерывание задерживается до возврата из текущей процедуры прерывания, несмотря на то, что новое прерывание имеет более высокий приоритет. По умолчанию процессор 80286 (в реальном режиме) при входе в процедуру прерывания запрещает прерывания. Это допустимо, если она состоит всего из нескольких команд, но длинная процедура должна сама разрешать прерывания, чтобы избегать задержки обслуживания прерывания с более высоким приоритетом. Разрешение прерываний осуществляет команда STI (установить флажок разрешения прерываний). Прерывания в виртуальном режиме. В виртуальном режиме таблица прерываний содержит дескрипторы различных обработчиков прерываний и называется дескрипторной таблицей прерываний IDT. Базовый адрес и предел таблицы IDT хранятся в регистре IDT. Сама IDT и регистр IDT показаны на рис. 5.22. Регистр ЮТ можно загружать и запоминать с помощью команд LIDT и SIDT, действующих аналогично рассмотренным ранее командам LGDT и SGDT. Таблица IDT, как и таблица GDT, одна. Содержимое регистра IDT не изменяется при переключении задач. 24 бита 16 бит База IDT | Предел IDT | Регистр ЮТ Шлюз для прерывания 1 (Предел IPT+ 1) /8 8 дескрипторов Шлюз для прерывания 2 Рис. 5.22. Дескрипторная таблица прерываний и регистр IDT Дескрипторная таблица прерываний (IDT) 211
Регистр ЮТ обычно загружается при инициализации системы и в дальнейшем не изменяется. Поскольку часть инициализации осуществляется при работе процессора 80286 в реальном режиме, команда Щ)Т в реальном режиме допустима. Фактически эта команда позволяет системному программисту перемещать таблицу прерываний или изменять ее предел, работая полностью в реальном режиме. Более того, особый случай "ЮТ слишком мала" (он невозможен, когда предел разрешает все 256 процедур прерываний) может возникать и в реальном режиме. Поскольку эти средства нарушают совместимость с микропроцессором 8086, маловероятно, что системные программисты воспользуются командой LIDT для чего-то еще, кроме подготовки к переключению в виртуальный режим. Однако такая возможность в реальном режиме упрощает схемы процессора 80286, увеличивая сходство между реальным и виртуальным режимами. Аналогичное обоснование относится и к особому случаю превышения сегмента. Ради совместимости с микропроцессором 8086 регистр IDT инициализируется на базовый адрес 0 и предел 256 х 4 - 1 = 1023 (каждый элемент в таблице прерываний имеет длину четыре байта и всего существует 256 типов прерываний). Перейдем, однако, к более подробному рассмотрению виртуального режима. Шлюзы в таблице ЮТ. Как показано на рис. 5.22, элементами таблицы ЮТ в виртуальном режиме являются дескрипторы шлюзов. Применение 8-байтных дескрипторов вместо 4-байтных адресов (как в реальном режиме) обеспечивает большую гибкость в вызове обработчиков прерываний. В ЮТ разрешены три типа шлюзов: шлюзы специальных прерываний, шлюзы прерываний и шлюзы задач; первые два новых типа разрешены только в ЮТ. Форматы шлюзов показаны на рис. 5.23. Использование шлюза того или иного типа зависит от причины прерывания. 16 бит а) 8 бит 8 бит Доступ feg^^i 32 бита Виртуальный адрес процедуры прерывания Рис. 5. 23. Формат шлюзов прерываний и специальных прерываний: a — формат дескрипторов прерываний и специальных прерываний, б — байт доступа шлюза специального прерывания, в —байт доступа шлюза прерывания *> |р| oMo.o.iTn в) j р | qpl | 0[ 0 [ 1 [ 1 \ 0 | 212
Шлюзы специальных прерываний - это стандартный вид шлюзов для особых случаев. Процедуры обработки особых случаев, адресуемые шлюзами специальных прерываний, вызываются как в реальном режиме, но при входе в них флажок разрешения прерываний IF остается неизменным. Именно это и требуется в процедуре обработки особого случая, так как нежелательно, чтобы особые случаи в задаче выключали механизм разделения времени операционной системы путем игнорирования прерываний таймера. Шлюзы задач - это стандартный вид шлюзов для внешних прерываний. Обработчики прерываний, адресуемые шлюзами задач, инициируются операцией вызова задачи, как было описано в разд. 5.4. Это именно то, что требуется при обработке прерываний от внешних устройств, так как обычно такие сигналы прерываний направляются не в текущую выполняющуюся программу, а в операционную систему. Неважно, какая пользовательская программа занимает процессор в момент появления сигнала прерывания, поэтому нет основания в выполнении обработчика прерывания как процедуры в некоторой пользовательской задаче. Если задача обработки прерывания выполняется с разрешенными прерываниями, она сама может быть прервана устройством с более высоким приоритетом. Такую ситуацию (вложенные вызовы задач) механизм вызова процессора 80286 обрабатывает правильно. Фактически главная причина введения в процессор вызова задачи заключается в том, чтобы обеспечить правильную и эффективную реализацию задач обработки прерываний. Шлюзы задач необходимо также использовать для обработки особых случаев, которые настолько разрушительны, что "поврежденная" задача не может осуществить вызов процедуры (примером служит особый случай недействительного сегмента TSS). В этой ситуации выполнение обработчика прерывания как отдельной задачи обеспечивает изолирование задач, требуемое для производства эффективного действия. Имеются ситуации, когда шлюзы задач нельзя использовать для внешних прерываний. Если обработчик прерывания состоит всего из нескольких команд, более быструю реакцию можно получить, применяя шлюз прерывания или шлюз специального прерывания (см. далее), которые реализуют быстрый вызов процедуры, а не переключение задач. Примером служит рассмотренная ранее процедура dispatch. Но если обработчик прерывания содержит больше команд, то различие во времени между вызовом процедуры и переключением задач становится незначительным по сравнению со временем выполнения тела обработчика прерывания. Последним типом шлюзов, разрешенных в ЮТ, являются шлюзы прерываний. Они действуют также, как и шлюзы специальных прерыва- 213
ний, но перед вызовом адресуемой процедуры они запрещают прерывания (сбрасывают флажок IF). Следовательно, переход через шлюз прерывания действует так же, как прерывание в реальном режиме, поэтому шлюзы прерываний можно применить для обеспечения совместимости с реальным режимом. Они же применяются, когда шлюз задачи нежелателен (см. выше). Мы рассмотрели различные типы дескрипторов и таблицы, в которых могут находиться дескрипторы. Табл. 5.5 показывает типы дескрипторов, где они размещены, и как байт доступа идентифицирует каждый тип. Таблица 5.5. Типы дескрипторов Байт доступа Тип дескриптора Место нахождения дескриптора ***00001 Сегмент локальной дескрипторной GDT таблицы ***0001* Сегмент состояния задачи GDT ***00100 Шлюз вызова GDT, LDT ***00101 Шлюз задачи GDT, LDT, IDT ***00110 Шлюз прерывания IDT ***00111 Шлюз специального прерывания IDT Сегмент данных GDT, LDT ***££*** Сегмент кода GDT, LDT * обозначает 0 или 1 Правила защиты для прерываний. Применение к прерыванию правил защиты зависит от его источника. Необходимость учета источника прерывания связана с тем, что требуется предотвратить выполнение в пользовательской программе команды INT с номером прерывания периферийного устройства, которая заставляет операционную систему предполагать наличие сигнала прерывания от устройства. Если источником прерывания является команда INT, действуют обычные правила привилегий: шлюз можно использовать только в том случае, если команда INT выполняется в кольце, содержащем шлюз, или в кольце с меньшим номером (более привилегированным). Таким обра- * зом, операционная система может защитить шлюзы ЮТ от использования несанкционированными командами INT. Если источником прерывания не является команда INT, то шлюзом можно пользоваться из любого кольца. Следовательно, особые случаи и прерывания от устройств, которые могут возникать в любой момент времени и в любом кольце, всегда обслуживаются правильно. 214
Некоторые правила защиты действуют независимо от источника прерывания. В случае шлюза специального прерывания или шлюза прерывания процедура прерывания (в отличие от шлюза) должна быть по меньшей мере столь же привилегирована, как и прерывающая программа, иначе возникает особый случай защиты, Это аналогично ограничению от вызовов "вверх", которое действует для шлюзов вызовов. На шлюзы задач такое ограничение не распространяется. Поскольку прерывания от устройств могут возникать в любой момент времени, правила требуют, чтобы все процедуры прерываний устройств находились в кольце 0. В этом скрыта еще одна причина использования для обработки прерываний устройств шлюзов задач, а не шлюзов специальных прерываний или прерываний. Обработка прерывания в зависимости от задачи. В некоторых языках высокого уровня, например ПЛ/1 и Ада, программистам разрешено определять собственные обработчики особых случаев. В системах, рассчитанных на такие языки, различные задачи могут потребовать разных обработчиков одного и того же особого случая. Поскольку имеется всего одна IDT, которую разделяют все задачи в системе, придется искать какие-то хитроумные решения. Одно из возможных решений заключается в том, чтобы ввести в операционную систему процедуру распределения прерываний. Все особые случаи, которые операционная система разрешает пользователям обрабатывать самим, проходят через распределитель: все шлюзы специальных прерываний для этих особях случаев в таблице IDT адресуют распределитель прерываний. Последний обращается к таблице, находящейся в локальном адресном пространстве пользовательской программы, и определяет, куда переходить для каждого из особых случаев, поскольку обращение к таблице осуществляется через LDT, она будет меняться в соответствии с задачей, из которой вызвана процедура распределителя прерываний. Сам распределитель прерываний должен находиться в подчиненном сегменте, чтобы он мог вызывать пользовательские непривилегированные обработчики без нарушения защиты. Еще одно решение - превратить в распределитель прерываний сам процессор 80286 (см. рис 5.24). Предположим, что три оссбых случая А, В и С разрешено обрабатывать пользователю. Мы устанавливаем соглашение о том, что в каждой задаче сегменты 0, 1 и 2 в таблице LDT всегда будут содержать обработчики А, В и С Поэтому мы помещаем селекторы таблицы LDT для сегментов 0, 1 и 2 в шлюзы специальных прерываний для прерываний А, В и С в ЮТ (напомним, что шлюз содержит виртуальный гдрес ~ сслзктор и смещение). Поскольку таблица LDT меняется от задачи к задаче, так же будут меняться и обработчики, как это показано иг рнс. 5,24.
Задача 1 (выполняется) ЮТ Прерывание А | Г| Прерывание В j f^j Прерывание С | ' [ -т-> ldt Обра J ботка CI Обработка В Обра ботка А Задача 2 ldt Обра-| ботка С Обработка В Обра-| ботка А Рис. 5.24. Обработка прерывания в зависимости от задачи Коды ошибок. В виртуальном режиме многие особые случаи обеспечивают обработчику дополнительную информацию (а не только адрес возврата), которая идентифицирует характер ошибки. Для таких особых случаев процессор 80286 включает в стек адрес возврата и 16-битный код ошибки, а затем переходит к обработчику особого случая. При использовании шлюза задачи адреса возврата нет, поэтому в стек включается только код ошибки. Формат кода ошибки показан на рис. 5.25. Код ошибки определяет, какой сегмент или шлюз является причиной ошибки. Сам код ошибки выглядит как селектор, но три младших бита, в которых должно быть поле RPL, имеют другую интерпретацию. Если бит I (ЮТ) кода ошибки содержит 1, то индекс относится к таблице ЮТ, а не к GDT или LDT, как в обычном селекторе. Если, например, прерывание индексирует сверх предела таблицы ЮТ, сформированный код ошибки будет иметь I = 1 и содержать недопустимый индекс. При 1=0 дескрипторная таблица определяется битом TI, как и в обычном селекторе. 3-15 2 1 о Индекс Рис 5.25. Формат кода ошибки 216
Если бит внешний ЕХТ кода ошибки содержит 1, то прерванная команда не "виновна" в генерировании особого случая. Когда, например, прерывание устройства вызывает обращение к отсутствующему (Р = 0) сегменту, в коде ошибки бит ЕХТ устанавливается в 1 и выбирает дескриптор сегмента, который отмечен как отсутствующий. Если ЕХТ « 0, ошибка скрыта в прерванной команде. Зарезервированные прерывания виртуального режима. В табл. 5.6 показаны все зарезервированные прерывания процессора 80286 в виртуальном режиме. Столбец адреса возврата сообщает, на что указывает адрес возврата обработчика особого случая относительно "виновной" команды. Столбец кода ошибки показывает, образует прерывание код ошибки или нет. Особый случай называется повторно запускаемым, если после устранения причины прерывания можно продолжить программу, повторно выполняя прерванную команду. Например, при возникновении ошибки деления можно всегда увеличить делитель и повторить команду деления. Но, с другой стороны, если команда ADS записывает в сегмент данных, из которого разрешено только считывать, возникающий особый случай не является повторно запускаемым. Команда ADC исказит флажок переноса прежде, чем будет зафиксирован особый случай. Поэтому, если разрешить запись в сегмент и повторить команду ADC, результат будет неверным. В табл. 5.6 показано, какие особые случаи являются повторно запускаемыми. Повторная запускаемость исключительно важна в особых случаях, возникающих из-за отсутствующих сегментов. В системе виртуальной памяти такой особый случай оказывается не ошибкой, а механизмом, запускающим свопинг; он заставляет операционную систему считать отсутствующий сегмент с диска в память и продолжать программу. Наверное, очень трудно и даже невозможно надежно реализовать виртуальную память в тех компьютерах, где особые случаи отсутствующего сегмента не являются повторно запускаемыми. В процессоре 80286 отсутствующие сегменты вызывают либо особый случай отсутствия, либо особый случай стека. Особый случай отсутствия всегда повторно запускаем, а особый случай стека оказывается таковым, если вызван отсутствующим сегментом (у него имеются и другие причины, когда повторный запуск невозможен). Поэтому обработчик особого случая стека в системе виртуальной памяти должен проверить бит Р ошибочного дескриптора сегмента стека и перейти к программе управления виртуальной памятью, если сегмент стека отсутствует (Р * 0). В противном случае (Р = 1) обработчик должен реагировать на особый случай сам. Из табл. 5.6 нам незнакомы только следующие два прерывания. 217
Таблица 5.6. Зарезервированные прерывания виртуального режима Повторная Прерывание Адрес возврата Код запускае» ошибки? мость? 0 Ошибка деления Первый байт команды Да Нет 1 ' Пошаговая работа Следующая команда Нет Нет 2 Немаскируемое прерывание Нет Да Нет 3 Контрольный останов Следующая команда Да Нет 4 Переполнение Следующая команда Да Нет 5 Превышен диапазон Первый байт команды Да Нет 6 Недействительный код Первый байт команды Да Нет операции 7 Отсутствие сопроцессора Первый байт команды Да Нет 8 Двойная ошибка Первый байт команды Нет Да 9 Превышение сегмента Не определен Нет Нет сопроцессором 10 Недействительный TSS Первый байт команды Да Да 11 Отсутствие (сегмента) Первый байт команды Да Да 12 Особый случай стека Первый байт команды Нет Да 13 Особый случай защиты Первый байт команды Нет Да 14 Зарезервировано - - - 15 Зарезервировано - • - - 16 Особый случай Следующая команда Нет Нет сопроцессора ЮСили WAIT 17 Зарезервированы 31 Двойная ошибка. Это очень серьезное прерывание возникает, когда появляется особый случай при попытке вызвать обработчик предыдущего особого случая. Если появляется третий особый случай при попытке вызвать обработчик особого случая двойной ошибки, процессор 80286 переходит в состояние отключения. В этом состоянии он прекращает свои действия и выводится из него сигналами сброса или немаскируемого прерывания. Назначение рассматриваемого особого случая - устранить возможность бесконечных циклов прерываний. Сначала шанс выйти из цикла дается программе (двойная ошибка), а если этого не достигается, срабаты- 218
вают схемы процессора (отключение). При отключении процессор может хотя бы отреагировать на немаскируемое прерывание в случае катастрофического события, чего может не быть в бесконечном цикле прерываний. Особый случай стека. Имеются две возможные причины особого случая стека. Мы уже рассмотрели такую причину, как отсутствие сегмента стека. Вторая причина связана с переполнением или антипереполнением стека. Здесь допускается повторный запуск, кроме некоторых переполнений, вызываемых командой PUSHA, и антипереполнений, вызываемых командой РОРА. Проблема усложняется тем, что эти команды включают и извлекают много байт данных и часть их может быть успешно передана, прежде чем возникает переполнение или антипереполнение. Поэтому, если команда выполняется повторно, придется включать или извлекать слишком много данных. 5.6. ИНИЦИАЛИЗАЦИЯ СИСТЕМЫ В книгах по микропроцессору 8086 материал по инициализации не нужен: чтобы начать выполнять программу, в нем необходимо только инициализировать регистры. Но мы уже видели, что для выполнения программ процессором 80286 в виртуальном режиме требуются специальные таблицы в памяти и необходимо инициализировать гораздо больше регистров. Процессор 80286 начинает работать в реальном режиме, а не в виртуальном, по следующей причине: реальный режим почти идентичен режиму работы микропроцессора 8086 и для выполнения программы не требует никаких подготовительных операций. Поэтому в реальном режиме можно задать все таблицы и регистры, необходимые для виртуального режима, а затем перейти в виртуальный. Следовательно, запуская процессор 80286 в реальном режиме, мы не только достигаем его совместимости с микропроцессором 8086, но и решаем архитектурную проблему инициализации виртуального режима. Далее мы рассмотрим некоторые тонкости и хитрости инициализации процессора 80286, а примеры программ инициализации можно найти в литературе. Начальное адресное пространство. На рис. 5.26 показаны начальные значения регистров SS, DS, ES, CS и IP. Они определяют начальное адресное пространство реального режима и адрес первой выполняемой команды. Интересно отметить, что начальные значения содержимого регистров SS, DS и ES равны нулю, как и в микропроцессоре 8086, а в нем они равны нулю ради совместимости с микропроцессором 8080. При внимательном рассмотрении рис. 5.26 могут появиться вопросы. 219
Рис. 5.26. Адресное пространство процессора 80286 при инициализации Максимально физическое адресное пространство в реальном режиме составляет Шбайт, но регистр CS адресует сегмент, занимающий старшие 64К байт в адресном пространстве 16Мбайт; как это получается? После операции сброса процессор 80286 искусственно добавляет четыре старших единичных бита к каждому 20-битному адресу, сформированному с привлечением регистра CS, что и дает показанный 24-битный адрес. Это соглашение относится только к регистру CS, но не к другим сегментным регистрам. Процессор 80286 действует таким образом до выполнения команды, которая изменяет регистр CS (например, межсегментный переход). После изменения содержимого регистра CS старшие единицы больше не добавляются и адресное пространство "сжимается" до обычных Ш байт. Странно! Но почему процессор 80286 не использует 20-битный адрес и не обращается к сегменту кода в старших 64К байтах обычного адресного пространства 1М байт реального режима? Регистр CS адресует сегмент кода, содержащий программу инициализации. Она не может находиться в обычном ЗУПВ, так как должна сохраняться при выключении питания, т. е. она должна находиться в ПЗУ. Поэтому, если оставить предлагаемое размещение, а затем переключиться в виртуальный режим, ПЗУ окажется где-то в середине адресного пространства. Разумеется, это усложнит программы распределения памяти и, возможно, схемы контроллера памяти. Но почему тогда не установить регистр CS на нуль и не хранить программу инициализации в начале памяти? В этом месте находится таблица прерываний реального режима. Обнаружение наличия сопроцессора. В некоторых системах предусматривается панелька для сопроцессора 80287, а сам сопроцессор может быть или не быть. Схемы процессора 80286 не могут автоматически обнаружить наличие или отсутствие сопроцессора. Программа должна 220
проверить это во время инициализации системы и соответственно установить биты ЕМ и MP в слове состояния машины. Для этого выполняется команда FNINIT (конечно, с ЕМ = 0), а затем команда FSTSW АХ. Если сопроцессор подключен, команда FNINIT инициализирует младшие 8 бит регистра состояния сопроцессора (флажки особых случаев) на нули, а команда FSTSW АХ передает в АХ содержимое регистра состояния. Если панелька для сопроцессора 80287 спроектирована по спецификациям фирмы Intel (см. гл. 6), то при пустой панельке гарантируется, что после выполнения этих двух команд в младших 8 битах регистра АХ будет по крайней мере одна единица. Переход в виртуальный режим. Процессор 80286 переключается из реального режима в виртуальный, загружая командой LMSW в слово состояния машины такое слово, в котором бит разрешения защиты РЕ равен 1. Рассмотрим некоторые тонкости этого переключения. Ясно, что переход возможен только после инициализации нужных регистров (GDT, LDT и др.) и таблиц (GDT, IDT и др.). Не столь очевидно, что начальная таблица GDT и сегмент состояния задачи не должны находиться в ПЗУ. Это связано с тем, что процессор попытается установить в 1 бит А в любом дескрипторе сегмента, к которому он обращается, и изменить бит В в любом сегменте состояния задачи, который он использует. Следовательно программа инициализации должна переслать эти элементы из ПЗУ инициализации в обычную память дЬ использования их в виртуальном режиме. До обсуждения следующей тонкости сделаем небольшое отступление. Напомним, что сопроцессор 80287 разделен на две части (устройство шинного интерфейса и численное операционное устройство), которые могут работать независимо друг от друга. Процессор 80286 разделен на четыре устройства, работающие параллельно (рис. 5.27). Шинное устройство последовательно считывает байты команд и помещает их в очередь. Командное устройство берет байты команд из очереди, декодирует их во внутреннюю форму и помещает эти внутренние команды еще в одну очередь. Операционное устройство выполняет внутренние команды. Адресное устройство преобразует виртуальные адреса в физические, которые передаются в Шинное устройство. Разделение процессора на параллельно работающие устройства значительно повышает его производительность. Труднее всего дело обстоит с командами переходов (и вызовов). Когда операционное устройство выполняет переход к другой ячейке, оно должно сообщить шинному устройству о том, что необходимо выбросить все байты команд из очереди, так как они находятся в памяти после команды перехода. По этой же причине становится бесполезной и очередь внутренних команд командного устройства. После этого операционное устройство должно ожидать до тех 221
Адресное устройство Физические адреса Реальные и виртуальные адреса г - - - - - - 1 Операционное устройство Операнды и результаты Внутренние команды • - - л - - - Командное 1 Q устройство В память Рис. 5.27. Функциональные устройства процессора 80286 пор, пока первая команда по адресу перехода не пройдет через шинное и командное устройства. Вот почему условный переход, если он действительно происходит, выполняется дольше. Предположим, что мы выполняем команду LMSW, переключающую процессор в виртуальный режим. После того, как операционное устройство закончило ее выполнение, в очереди внутренних команд еще буду! находиться внутренние команды, сформированные командным устройством в предположении, что процессор работает в реальном режиме. Бели операционное устройство выполнит их в виртуальном режиме, получатся неверные результаты. Решение проблемы заключается в том, чтобы после команды LMSW вставить команду JMP внутрисегментного (короткого) перехода. Она будет выполнена правильно несмотря на то, что декодировалась в реальном режиме, а выполнена в виртуальном. Регистр CS вновь инициализировать не нужно. Даже кажущаяся бесполезной команда JMP перехода к следующей команде осуществляет требуемые действия. Команда JMP вызовет сброс всех очередей и все последующие команды правильно декодируются во внутренние команды виртуального режима. 222
Отметим, что после перевода процессора 80286 в виртуальный режим вернуть его в реальный можно только сигналом сброса. Обратное переключение не производят ни команда LMSW, ни какая-нибудь другая команда. Популярный аппаратный способ обхода этого ограничения заключается в том, чтобы подключить сигнал сброса процессора к одному из его выходных портов. Тогда программа может вернуть процессор в реальный режим, осуществив вывод в этот порт. ЗАКЛЮЧЕНИЕ На этом мы заканчиваем обсуждение архитектур процессоров 80286 и 80287. Пришло время рассмотреть, как эти две микросхемы объединяются в законченную систему. Об этом речь пойдет в следующей главе. ГЛАВА 6 ПРОЕКТИРОВАНИЕ КОМПЬЮТЕРА Микросхема 80286 - это только центральный процессор (ЦП), а не законченный компьютер, для построения которого необходимо подключить память и устройства ввода-вывода. Для управления несколькими прерываниями потребуется контроллер прерываний. Если нужны вычисления с плавающей точкой, придется подключить сопроцессор 80287. В этой главе мы рассмотрим, как все эти компоненты подсоединяются к процессору 80286. Предполагается, что читатель знаком с логическими элементами (И, ИЛИ, И-НЕ, ИЛИ-НЕ и др.), а других сведений по логическому проектированию не потребуется. За основной критерий при выборе схем мы приняли простоту, поэтому главу можно еще назвать "Компьютерные схемы для Начинающих". Однако содержащийся здесь материал дает глубокое знание аппаратных средств процессора 80286, и после его изучения вы уже не будете начинающим. . Сначала мы рассмотрим, как процессор взаимодействует с внешними устройствами и как выглядит временная диаграмма его работы. Изучив эту общую информацию, мы перейдем к конкретным устройствам, причем не будем вдаваться в детали их работы, а сосредоточимся на их интерфейсе с процессором. Наиболее простым из подключаемых устройств является ПЗУ, поэтому мы начнем с ПЗУ небольшой емкости для инициализации системы. Затем на трех примерах рассмотрим подключение устройства ввода-вывода. Первый пример относится к программируемому интервальному таймеру, который потребовался в системе с разделением времени (см. гл. 5). Во втором примере речь идет о программируемом контроллере прерываний 8259А, о котором мы также упомянули в гл. 5. Последний пример посвящен быстродействующему каналу прямого доступа к памяти (ПДП). Затем мы затронем наиболее часто применяемую в персональных компьютерах и небольших системах память — динамические ЗУПВ. После знакомства с принципами работы такой памяти мы покажем, как она подключается к процессору 80286*. Глава заканчивается рассмотрением интерфейса процессоров 80286 и 80287. 223
6.1. ШИНА ПРОЦЕССОРА 80286 На рис. 6.1 показано, как выглядит система на базе процессора 80286 с точки зрения программиста. В центре находится процессор, а все остальные устройства соединяются с ним отдельными линиями связи. Практически такую систему реализовать нельзя, так как микросхема 80286 имеет ограниченное число контактов, связывающих ее с внешним миром (рис. 6.2). Если применить общую схему, показанную на рис 6.1, то придется ограничить число внешних устройств, так как для них потребуются отдельные контакты корпуса микросхемы. Чтобы снять это ограничение, Сигналы прерываний U-1 Порт Устройство ввода- 1 вывода 1 Процессор 80286 • • • Порт Устройство ввода- 1 вывода 1 I Память Рис. 6.1. Система на базе процессора 80286 с точки зрения программиста 000® 0 0 0 0 0 0© 00 00 00 00 00 00 0© 9 0® © © © 0000 0 0 0 0©® 0 0 0 0 0®® ^ ®® 0© 00 0© ©® ®® ®® ©0 0 0 00 0 0 0 0 0 0 00 0 224 Рис. 6.2. Контакты процессора 80286
Линия запроса прерывания Рис. 6.3. Шина процессора 80286 процессор 80286 подключается к шине, как показано на рис. 6.3. Шина - это группа проводников (металлических полосок на печатной плате), проходящих через всю систему; к ним подключаются все устройства. Линии шины подразделяются на три группы: шина адреса, шины данных и шина управления. Каждому устройству назначается адрес, а таким устройствам, как память, - диапазон адресов. Для взаимодействия с конкретным устройством процессор выдает на 24 линии шины адреса логические значения (0 и 1), соответствующие битам адреса этого устройства. Хотя адрес подается во все устройства, только адресуемое устройство распознает свой собственный и реагирует на него. Если процессор выдает информацию, он заставляет логические значения на 16 линиях шины данных соответствовать передаваемому слову. Если же процессор принимает информацию, то значения на линиях шины данных устанавливает адресуемое устройство. В обоих случаях процессор сигналами на линиях шины управления показывает, принимает он информацию или передает и является ли адресуемое устройство памятью или устройством ввода-вывода. Линии шины. На рис 6.4 показаны сигналы процессора 80286, относящиеся к шине. Адрес выдается на линии А0 - А23. В операциях с памятью адрес является физическим (см. гл. 5). В реальном режиме адреса содержат только 20 бит, а не 24. Поэтому при работе процессора в реальном режиме для памяти важны только линии AQ - А19. В операциях ввода-вывода адрес является номером порта (см. гл. 2). Так как номера портов имеют длину 16 бит, для устройства ввода-вывода важны только линии Ал - А . О 15 225 8 Заказ 6126
Состояние цикла шины Процессор 80286 Рис. 6.4. Интерфейс шины процессора 80286 Линии D0 - D15 несут данные, которые передаются или принимаются в цикле шины. Четыре линии состояния SI, SO, COD/INTA и М/Ю показывают характер цикла шины и служат линиями управления шиной. В табл. 6.1 показано, как интерпретировать линии состояния цикла шины. Сигналы SO и S1 показывают, что делает процессор: принимает (считывание или ввод), передает (запись или вывод) или что-то еще (прерывание, останов или отключение). Когда процессор передает или принимает, сигнал М/Ю показывает, производится операция с памятью или с устройством ввода-вывода. Когда процессор считывает из памяти, сигнал COD/INTA показывает, производит он выборку команд (т. е. кода) или считывание данных. Отметим черту над сигналом S1 на рис. 6.4. Она означает, что на линии действует логическая инверсия сигнала, т. е. НЕ S1. Ради простоты мы опускаем эти детали из рассмотрения (но не из рисунков). 226
Таблица 6.1. Коды состоянии цикла шины Операция S1 SO МДО COD/INTA Цикл шины Ввод Процессор принимает Процессор выдает Остальные 1 0 Считывание из памяти (данные) 1 Считывание из памяти (команды) 0 1 Вывод 1 0 Запись в память 0 0 Подтверждение прерывания ^ 1 0 Останов или отключение At = 1 для останова, Аг - О для отключения Временная диаграмма циклов шины. В системе на базе процессора 80286 для измерения времени применяются три единицы: такты системной синхронизации, циклы процессора и циклы шины. Основным временным сигналом в системе является сигнал системной синхронизации CLK. Подобно ударам метронома он помогает всем компонентам системы действовать в правильные моменты времени. Период повторения сигнала CLK называется тактом системной синхронизации. При частоте синхронизации 16 МГц такт составляет 62,5 не. Два такта системной синхронизации образуют цикл процессора, причем первый из них называется фазой 1, а второй - фазой 2 (рис. 6.5). Следовательно, процессор 80286 работает на частоте, которая в два раза меньше частоты системной синхронизации. Например, при работе процессора на частоте 8 МГц частота системной синхронизации равна 16 МГц, а цикл процессора длится 125 не. Далее мы покажем, почему частота системной синхронизации в два раза выше частоты работы процессора. 62.5 не Время gj- Такты системной синхронизации | CLK | CLK | CLK | CLK | CLK | CLK | CLK | CLK | 125 нс t * | Фаза 11 фаза 2J Фаза 1 Фаза 2| Фаза 1 Фаза 2| Фаза l| Фаза 2| 125 не t * Циклы процессора 250 не Ч Циклы шины Состояние j Команда | Состояние | Команда | Рис. 6.5. Временная диаграмма шины 227
Каждый цикл шины включает в себя два действия и потому занимает, по крайней мере, два цикла процессора. В первом цикле процессор выдает состояние шины и адресную информацию, что объясняет название этого цикла - цикл состояния. Подключенные к шине устройства должны разобраться, как им реагировать на обращение процессора. В следующем цикле процессора, называемом командным циклом, процессор и адресуемое устройство производят передачу данных. Некоторые устройства (ввода-вывода или памяти) не могут отреагировать к моменту окончания командной части цикла шины. Можно, конечно, уменьшить системную синхронизацию так, чтобы самое медленное устройство успевало отреагировать вовремя. Однако есть другое, более приемлемое решение. У процессора предусмотрен вход готовности READY. Если устройство сигнализирует о неготовности во время командной части цикла шины, процессор расширяет ее на дополнительный цикл процессора, который называется состоянием ожидания. Если устройство продолжает сигнализировать о неготовности, вводятся дополнительные состояния ожидания (рис. 6.6). *) Состояние I Команда Расширенный интервал для выполнения команды устройством б) г > Состояние Команда Команда Команда к у А. Состояние ожидания Рис. 6.6. Расширение цикла шины с помощью состояний ожидания: й — обычный цикл шины, б — цикл шины, расширенный на два состояния ожидания —V— Состояние ожидания Вместо введения состояний ожидания только для тех устройств, которые нуждаются в них, можно добавить фиксированное число состояний ожидания к каждому циклу шины. Представляется, что этот прием не лучше понижения частоты системной синхронизации, но иногда он предпочтительнее. Дело в том, что многие команды оперируют только регистрами и не требуют никаких перадач по шине. На такие команды состояния ожидания не повлияют. Более того, команды типа деления DIV выполняются столь долго, что состояние ожидания пренебрежимо мало. Измерения по бенчмарк-программам показали, что добавление одного состояния ожидания к каждому циклу шины ухудшает производительность процессора примерно на 25 %, хотя время цикла шины увеличивается на 50 %. Подведем итоги. Минимальной измеримой единицей времени является такт системной синхронизации. Каждый цикл процессора занимает 228
точно два такта системной синхронизации. Каждый цикл шины имеет длительность, равную, по крайней мере, двум циклам процессора. Циклы шины состоят из командной части (один или несколько циклов процессора) и части состояния (один цикл процессора). Командная часть расширяется сверх одного цикла процессора путем введения состояний ожидания. Генерирование импульсов синхронизации. Импульсы системной синхронизации вырабатывает не процессор 80286, а микросхема 82284 генератора синхронизации. На рис 6.7 показано ее подключение к процессору. Частота сигнала системной синхронизации определяется резонансной частотой кварца. Для изменения ее необходимо заменить только кварц. Кварц Системная синхронизаци Системный сброс Для ввода состоянии ожидания HDh Х2 Х1 RES RESET SRDY READY SO si Генератор синхро - низации 82284 PCLK 1 A23-A0 D15-D0 CLK RESET READY SO §1 Процессор 80286 Синхронизация процессора Рис. 6.7. Подключение генератора синхронизации к процессору Кроме системной синхронизации CLK генератор формирует импульсы, соответствующие циклам процессора PCLK для обслуживания остальной части системы. Чтобы сигнал PCLK правильно отражал один цикл процессора» а не фазу 2 одного цикла и фазу 1 следующего, необходимо синхронизировать генератор 82284 и процессор. Для этого все сигналы системного сброса подаются в микросхему 82284, а затем поступают в процессор. Эти сигналы и обеспечивают синхронизацию двух микросхем. Еще одна функция генератора синхронизации касается состояний ожидания. Для введения состояния ожидания процессор требует, чтобы сигнал READY отсутствовал в точный момент цикла шины и в течение определенного интервала. При подаче сигналов READY в генератор он помогает в синхронизации, поэтому временная точность в запросах состояний ожидания может быть меньше. 229
Конвейеризация шины. Стоимость компьютерной памяти зависит от ее быстродействия: чем меньше время обращения, тем выше удельная стоимость. В процессоре 80286 применяется интересный способ, называемый конвейеризацией шины, для повышения производительности при медленной памяти, хотя для этого приходится усложнять схемы. При обсуждении цикла шины мы предполагали, что процессор выдает адрес на шину адреса в начале части состояния цикла шины и сохраняет его в течение всего цикла шины. Мы предполагали также, что данные помещаются на шину в командной части и сохраняются на ней до конца цикла шины. Но теперь представим себе, что адрес выдается немного раньше начала цикла шины и не будем требовать данных даже немного спустя после начала командной части цикла. Очевидно, при этом на реакцию устройствам отводится больше времени. Рассмотрим этот способ подробнее. На рис. 6.8,(i показано, что в действительности происходит на трех частях шины процессора (линиях состояния, шине адреса и шине данных). Предполагается, что частота синхронизации процессора составляет 8 МГц. Видно, что адрес опережает цикл шины на один такт системной синхронизации (62,5 не). На такой же интервал данные "залезают" в следующий цикл шины. (Отметим, что здесь используется дополнительная разрешаю- Цикл шины 1 250 не Цикл шины 2 Цикл шины 3 Линии состояния цикла шины. Состояние 1 Команда 1 Состоян ие 2|Команда 2 Состояние 3[Команда Шина адреса Шина данных 250 не л Адрес 1 Адрес 2 I Адрес 3 Т 375 не _-А |Данны Устройство А Устройство А | Зафиксированный адрес 1 | £ Зафиксированный адрес i • I « J Устройство В I Зафиксированный адрес 2 250 не Шина адреса с защелкой . Зафиксированный 6) jaAP&cl «Зафиксированный —— адрес 2—, IЗафиксированный адрес 3 , в) Рис. 6.8. Временная диаграмма конвейерной шины 230
щая способность по времени, обеспечиваемая двойной частотой системной синхронизации.) Большинство устройств работают неправильно, если их адрес исчезает в середине цикла шины. Но на рис. 6.8,о видно, что именно это и происходит на шине процессора, когда адрес для одного цикла шины освобождает место для следующего адреса. Устройство должно зафиксировать свой адрес, как только он появляется на шине адреса, и должно помнить о том, что оно адресовано до окончания цикла шины. Предположим, что каждое устройство подключено к шине адреса через защелку (см. микросхему ALS573 на рис. 6.9). Когда на входе строба С появляется сигнал, защелка запоминает значения восьми сигналов на входах D0 - D7. Запомненный байт подается на входы Q0 - Q7, если активен сигнал разрешения выхода ОЕ. Изменения входных сигналов не влияют на выходные сигналы до нового стробирования. При необходимости три микросхемы ALS573 запоминаю!' все 24 бита адреса. Мы предполагаем, что каждое устройство имеет свою защелку, поэтому адресуемое устройство стробирует свою защелку, когда адрес впервые появляется на шине, и адрес сохраняется защелкой до тех пор, пока устройство не заканчивает операцию с данными (см. рис. 6.8,6). На рис. 6.8,6 предполагается, что устройство А адресуется в первом цикле шины, устройство В - во втором и снова устройство А - в третьем. Теперь на одну передачу по шине каждое устройство имеет не 250 не (продолжительность цикла шины), а 375 не. При этом не введено ни одного состояния ожидания и частота синхронизации не уменьшена. На первый взгляд кажется, что мы получили что-то из ничего. Подозрительный читатель заметит, что никакого выигрыша от защелок нет. Конечно, выигрыша нет, если два цикла шины обращаются к одному и тому же устройству, один сразу после другого (см. рис. 6.8,6). Вход данных D0-D7 Q0-Q7 Выход данных Защелка ALS573 С ОЕ ^—г Строб Разрешение выхода Рис. 6.9.8-битная защелка ALS573 231
В случае устройств ввода-вывода программист может легко обеспечить, чтобы команды IN и OUT, обращающиеся к одному и тому же порту, не были слишком близки друг к другу. Однако для памяти такая задача достойна Геркулеса. Поэтому потребуются дополнительные схемы для введения состояния ожидания во второй цикл каждой пары циклов шины, в которых обращения производятся к одному и тому же запоминающему устройству. Мы вернемся к этому вопросу при обсуждении динамических ЗУПВ. Еще один недостаток конвейеризации шины заключается в том, что в каждом устройстве должна быть своя защелка. Конечно, мы можем упростить интерфейс устройств с шиной, если предусмотреть одну защелку для всей системы, но при этом теряются достоинства конвейеризации (см. временную диаграмму на рис 6.8,в). В этом случае шина процессора 80286 напоминает шины других микропроцессоров, например 8086. Защелки в каждом устройстве не нужны и не требуется вводить состояния ожидания в последовательных циклах шины с обращением к одному и тому же устройству. Буферирование шины данных. В большой вычислительной системе мощности процессора 80286 может не хватить для управления всеми устройствами, подключенными к шине. Для шины адреса эта проблема решается с помощью мощной защелки (см. выше). Однако защелка для шины данных не подходит, так как в зависимости от типа цикла шины данные передаются по одним и тем же линиям в обоих направлениях. Для шины данных вместо защелки требуется приемопередатчик. Это мощный двунаправленный усилитель, который передает сигналы в любом направлении, превращая выходные сигналы в более мощные, чем входные. Приемопередатчик ALS245 показан на рис. 6.10. Он имеет два набора линий данных AQ - А? и BQ - В?. Когда сигнал DIR (направление) активен, данные передаются слева направо - от А к В. Если же DIR = 0, дан- Вход или выход данных А0-А7 В0-В7 Приемопередатчик ALS245 DIR ОЕ т Вход или выход данных Направление Разрешение выхода Рис. 6.10. 8-битный приемопередатчик ALS245 232
ные передаются справа налево - от В к А. В любом случае для производства передачи сигнал ОЕ (разрешение выхода) должен быть активным. Шинные команды. Может показаться, что для стробирования защелок, разрешения приемопередатчиков и декодирования состояний шины в точные моменты времени потребуются сложные схемы. Фактически же все эти действия осуществляет микросхема 82288, называемая контроллером шины. На рис. 6.11 показано, как подключить контроллер шины. Контроллер шины воспринимает состояние шины с выходов SO, SI и М/Ю процессора. Он также воспринимает сигнал READY для правильного ввода состояния ожидания. Контроллер превращает входные сигналы в отдельные сигналы шинных команд: считывание из памяти MRDC, запись в память MWTC, считывание ввода-вывода IORC, запись ввода-вывода IOWC и подтверждение прерывания INTA. Пользоваться отдельными сигналами намного проще, чем "сырыми" сигналами состояния от процессора 80286. ready clk Генератор синхронизации 82284 srdy а0-а23 Процессор 80286 clk ready d0-d15 SO SI м/io л SO S1 М/Ю Контроллер шины 82288 ale mrdc mwtc Шс iowc Tnta Буферированная шина данных Считывание из памяти | Запись в память Считывание ввода-вывода Запись ) ввода-вывода Г Подтверждение] прерывания ^ на V Шинные команды -•^Системная синхронизация ——————— Для состояний ожидания Рис. 6.11. Применение контроллера шины 82288 233
Контроллер шины формирует сигнал разрешения защелки адреса ALE для стробирования защелок на шине адреса в правильные моменты времени. Мы не будем пользоваться входами ОЕ защелок, поэтому они заземляются: земля (О В) соответствует логическому значению FALSE (ложь), a Vcc (+5 В) соответствует логическому значению TRUE (истине). (Vcc - постоянное напряжение от источника питания.) Сигнал разрешения данных DEN контроллера шины разрешает приемопередатчики на шине данных, когда происходит передача данных. Сигнал передачи/приема данных DT/R показывает приемопередатчикам правильное направление передачи информации. Теперь временная диаграмма становится довольно простой. Вот основные этапы операции считывания: 1. Фиксируется адрес. 2. Задается направление включения приемопередатчиков шины данных, разрешается работа приемопередатчиков и выдается соответствующая команда считывания (IORC или MRDC). 3. Теперь адресуемое устройство может посылать данные. 4. Снимается сигнал на командной линии и запрещается работа приемопередатчиков шины данных. Основные операции записи также довольно просты: 1. Фиксируется адрес, задается направление включения приемопередатчиков и разрешается их работа. 2. Выдается соответствующая команда записи (IOWC или MWTC). 3. Адресуемое устройство может теперь принять данные. 4. Снимается сигнал на командной линии. 5. Запрещается работа приемопередатчиков. 6.2. ПОСТОЯННЫЕ ЗАПОМИНАЮЩИЕ УСТРОЙСТВА ПЗУ применяются в компьютерах потому, что они сохраняют данные при выключении питания. Побочным эффектом этого свойства оказывается невозможность записи. ПЗУ оформляются в виде кассет, которые используются как носитель для распространения программного обеспечения, либо встраиваются в компьютер. Имеется довольно много микросхем ПЗУ. Чем ближе микросхема к возможности записи, тем, при прочих равных условиях, она дороже. Самыми дешевыми являются обычные ПЗУ, запись в которые осуществляется в ходе технологического процесса производства и в дальнейшем изменить их содержимое невозможно. Эти ПЗУ экономичны только при заказе в больших количествах. Микросхемы программируемых ПЗУ (ППЗУ) выпускаются "чистыми" и допускают запись с помощью программатора. После того, как ППЗУ 234
запрограммировано, стереть его содержимое невозможно. Микросхемы стираемых ППЗУ (СППЗУ) имеют небольшое окно, через которое их содержимое можно стереть ультрафиолетовым светом. После стирания их можно репрограммировать электрическими сигналами с помощью программатора. Электрически стираемые ППЗУ (ЭСППЗУ) похожи на ССПЗУ, но стереть их содержимое можно электрическим сигналом. Микросхема энергонезависимого ЗУПВ (ЭЗУПВ) разделена на две части: одна ее половина - это обычное ЗУПВ, а вторая - ЭСППЗУ. ЗУПВ теряет свои данные при выключении питания, а ЭСППЗУ не теряет. При подаче в ЭЗУПВ команд считывания или записи их воспринимает часть ЗУПВ. Когда в микросхему подается специальный сигнал STORE (сохранить), например до отключения питания, все содержимое ЗУПВ передается в ЭСППЗУ. При подаче другого специального сигнала RECALL (возвратить), например после восстановления питания, осуществляется передача ключение к шине ППЗУ, в котором хранится программа инициализации. Микросхема 28S166 показана на рис. 6.12; емкость этой микросхемы составляет 16К бит с организацией 2К х 8. Микросхема воспринимает 11-битный адрес на входах А0 - А10 и выдает адресуемый байт на выходы Q0 - Q7. Сигналы Gl, G2 и G3 объединены по И для получения сигнала разрешения выхода микросхемы. Для программы инициализации мы используем две микросхемы 28S166 с общей емкостью 4К байт или 2К слов. Напомним из гл. 5, что ПЗУ инициализации должно находиться в самой верхней части физического адресного пространства 16М байт, поэтому ППЗУ должны реагировать на такие 24-битные адреса, в которых 12 старших бит содержат единицы. Остальные биты осуществляют адресацию внутри ППЗУ. из ЭСППЗУ в ЗУПВ. ПЗУ инициализ Как пример применения ПЗУ мы покажем под- ППЗУ 28S166 Адрес | (11 6ИТ)1 А0-А10 Q0-Q7 JV Данные 1|/(8 бит) G1 G2 G3 Разрешения выходов (объединяются по И) Рис. 6.12. ППЗУ 28S166 2К X 8 235
Выбор кристалла Шина адреса с защелкой НЕ-И и Vcc (логический уровень - 1) пли | Буферированная шина данных . • . I 2п -1 | 2п-1 2п 12п + 11STTS|| 2пчгЗ | Старший байт D8-D15 Рис 6.13. Подключение к шине ППЗУ 4К байт Для считывания слова из ячейки 2/1+1 процессор сначала считывает слово из ячейки 2л и игнорирует первый байт. 2n-1 Н 2п В2п+11 2п + 2 2п + 3 • • . 2п + 1 затем он считывает слово из ячейки 2п+ 2 и игнорирует второй байт 2п-1 2п 2п + 1 2п + 2 J Рис. 6.14. Считывание слова по нечетному адресу На рис. 6.13 показано, как подключить микросхемы ППЗУ к шине. Элемент НЕ-И определяет, находится ли адрес на шине внутри диапазона, отведенного для ППЗУ. Это называется дешифрированием адреса. Если адрес находится в диапазоне, формируется сигнал выбора кристалла. Он объединяется по И с сигналом MRDC от контроллера шины для разрешения микросхем ППЗУ. Одна из них выдает младший байт, а вторая - старший байт слова, передаваемого по шине данных. 236
Отметим, что младший бит адреса А0 не используется. Это объясняется тем, что процессор 80286 считывает только слова, которые начинаются по четным адресам. Бели команда заставляет процессор считать слово по нечетному адресу, он автоматически превращает эту операцию в два считывания по четным адресам (рис. 6.14) и игнорирует лишнюю информацию. Поэтому программы, в которых слова данных выравнены по четным адресам, выполняются быстрее. Причина того, что процессор 80286 никогда не считывает слова по нечетным адресам, заключается в упрощении интерфейса с шиной памяти и устройств ввода-вывода с сохранением в большинстве случаев более высокой производительности 16-битной шины над 8-битной. Например, выборку всех команд процессор осуществляет путем считывания слов по четным адресам. Только переходы по нечетным адресам несколько растягиваются из-за более сложного считывания из памяти. 6.3. УСТРОЙСТВА BBOДА-ВЫВОДА1 Интерфейс с шиной интервального таймера. В первом примере подключения к шине устройств ввод-вывода мы рассмотрим интерфейс программируемого интервального таймера 8254. Микросхема 8254 показана на рис. 6.15. Фактически в ней есть три таймера с номерами 0-2. Импульсы синхронизации для работы таймеров подаются на входы CLK0, CLK1 и CLK2. Каждый из таймеров формирует сигнал прерывания на одном из выходов OUTO, OUT1 и OUT2. Рис. 6.15. Программируемый интервальный таймер 8254 RD D0-D7 WR АО OUT0 Таймер 8254 Л. _ OUT1 А1 OUT 2 CS CLKO CLK 1 CLK2 t t t 1 Точнее, интерфейсные микросхемы. —Прим. перев. 237
Таймер 8254 имеет много режимов работы. Для разделения времени в мультизадачной системе необходимо выбрать режим генератора часто- гы.В этом режиме таймер 8254 периодически формирует сигналы прерываний. Продолжительность периода можно задавать программно. Мы не будем подробно обсуждать режимы работы микросхемы 8254; желающие познакомиться с ними могут обратиться к материалам фирмы. В микросхеме 8254 имеется регистр управления, который определяет режим, и регистры для хранения периодов трех таймеров. Доступ ко всем регистрам осуществляется по линиям D0 - D7. Сигналы на линиях А0 и Ах выбирают, какой из регистров подключается к линиям данных (см. табл. 6.2). Сигналы RD и WR производят считывания из адресуемого регистра или запись в него. Вход выбора кристалла CS разрешает считывание или запись. Таблица 6.2. Адресация регистров таймера 8254 Ах А0 Выбирают 0 0 Период 0 0 1 Период 1 1 0 Период 2 1 1 Регистр управления Мы подключим каждый из регистров к разным портам ввода-вывода процессора, чтобы программа могла производить считывание и запись командами IN и OUT соответственно. Назначим адреса портов следующим образом: 8 л период О 8п + 2 период 1 8 л + 4 период 2 8л + 6 регистр управления Так как номера портов состоят из 16 бит, старшие 16 бит номера порта будут л, следующие два бита будут выбирать регистр, а младший бит будет 0: Номер порта ХХХХХХХХХХХХХ ГГ О Регистр Как же дешифрировать адреса? Можно, например, построить схему из логических элементов для распознавания номера п. Такой вариант мы 238
видели в предыдущем разделе, где значение л было очень простым (все единицы). Но здесь мы воспользуемся более гибким способом. На рис. 6.16 показан программируемый компаратор тождества ALS526. Как и при программировании ППЗУ, в компаратор можно запрограммировать любое 16-битное число Q. Если 16-битное число на входах Р0 - Р15 равно запрограммированному числу Q и если микросхема разрешена по входу G, то на выходе будет сигнал TRUE. Рис. 6.16. Программируемый 16-битный компаратор ALS526 Рис 6.17. Подключение таймера 8254 к шине ALS526 Р0-Р15 Программируемый компаратор ALS526 t Разрешение —► P = Q, где Q - запрограммировано А1 А2 ДО ДЗ-А15 Шина адреса с защелкой РО, РЗ-Р15 Р1 Р2 Программируемый — G компаратор ALS526 (5r5 owe На рис 6.17 показано, как подключить таймер к шине. Биты А± и Aq с шины адреса выбирают соответствующий регистр 8254. Биты А3 - А15 сравниваются со значением п, которое запрограммировано в биты Q3 - Q15 программируемого компаратора. Бит А0 сравнивается с Q0, который должен быть запрограммирован на 0. Входы G, ?х и Р2 не используются, 239
поэтому они заземлены. Биты Qt и Q2 должны быть запрограммированы на 0. Все три входа синхронизации подключены к сигналу синхронизации процессора PCLK (здесь не требуется более высокая разрешающая способность сигнала CLK), а сигналы IORC и IOWC подаются от контроллера шины. Выходные сигналы таймера 8254 подаются в контроллер прерываний 8259А, который рассматривается далее. Ввод-вывод, отображенный на память. Предположим, что на рис. 6.17 к таймеру 8254 вместо сигналов ввода-вывода IORC и IOWC подключены командные сигналы памяти MRDC и MWTC. Предположим далее, что памяти по физическим адресам, равным номерам портов, нет, поэтому два устройства на шине одновременно реагировать не могут. Поскольку процессор 80286 осуществляет операции считывания/записи памяти аналогично соответствующим операциям ввода-вывода, регистры таймера 8254 будут "выдавать себя" за память. Считывание из памяти будет считыванием из регистров таймера, а запись в память - записью в регистры. Так реализуется ввод-вывод, отображенный на память. Преимущество ввода-вывода, отображенного на память, по сравнению с обычным вводом-выводом заключается в том, что можно использовать все средства преобразования адреса виртуального режима для размещения каждого устройства ввода-вывода, отображенного на память, в его собственном сегменте. После этого каждое устройство можно защищать отдельно, а программам управления устройствами разрешается доступ только к "своим" устройствам, а не ко всем. Недостаток ввода-вывода, отображенного на память, - иногда требуются более сложные схемы дешифрирования адреса. Так как номера портов имеют 16 бит, схема на рис. 6.17 реагирует только на сигналы адреса А0 - А15. Физические адреса состоят из 24 бит, поэтому при отображении таймера 8254 на память он появится по множеству различных физических адресов, если не ввести схемы контроля лишних 8 бит. Подключение контроллера прерываний. Процессор 80286 имеет два входа прерываний: NMI - немаскируемое прерывание и INTR - запрос прерывания. Сигнал на входе NMI заставляет процессор вызвать процедуру прерывания для прерывания с номером 2. Сигнал INTR позволяет внешним устройствам передать в процессор по шине данных однобайтный номер прерывания. Благодаря этому процессор может обслуживать до 256 различных типов прерываний, не требуя 256 входов запросов прерываний. Обычно на вход INTR подается выходной сигнал программируемого контроллера прерываний 8259А, а не запросы прерывающих устройств. Контроллер собирает запросы прерываний от множества устройств и передает их по одному в процессор. Он воспринимает запросы прерыва- 240
ний от внешних устройств (включая й другие микросхемы 8259А), учитывает их приоритеты, а затем сигнализирует процессору по входу INTR. Реагируя на сигнал, процессор заканчивает выполнение текущей команды, а затем инициирует два цикла шины подтверждения прерывания INTA. Первый цикл шины INTA информирует контроллер прерываний о том, что процессор распознал запрос прерывания. Он также отводит время контроллеру для взаимодействия с подключенными к нему другими контроллерами. В течение второго цикла шины INTA контроллер выдает в процессор по шине 8-битный номер прерывания. После этого процессор вызывает соответствующую процедуру обработки прерывания. Подключения контроллера прерываний в системе показаны на рис 6.18. Запросы прерываний подаются в него по восьми входам IR0 - IR7. Следовательно, один контроллер может обработать до восьми видов прерываний (восьми различных номеров прерываний). При наличиии более восьми типов прерываний потребуется несколько контроллеров. intr §1 м/Ю Процессор 80286 а0-а23 so si м/Ю Контроллер Ю+Ю шины 82288 iowc inta ale DTfR den 1 Запросы прерываний 4l int ir0-ir7 Контроллер прерываний 8259А rd wr кга ао dc457 cs лтттл P-Q Программируемый сомпаратор | ALS526 Щ—1 р2-р15.р0 Gf—t I am 5.ао ► Шина d0-d7 Шина Рис. 6.18. Подключение контроллера прерываний 8259А к процессору 80286 Контроллер 8259А требует два порта ввода-вывода, чтобы программы могли считывать и устанавливать разнообразные режимы его работы, обращаться к его внутренним регистрам и сообщать об окончании процедуры прерывания (см. гл. 5). Вход А0 определяет, какой из двух портов адресуется, а сигналы RD, WR, CS и D действуют так же, как и в таймере. 241
caso iro cas1 |r1 cas2 ir2 Контроллер ir3 ir4 irs ir6 ir7 int прерывании 8259A (ведомый) Каскадирование контроллеров прерываний. Чтобы обрабатывать более восьми видов прерываний, требуется несколько контроллеров прерываний. Только один из них, называемый ведущим, соединяется со входом INTR процессора. Остальные контроллеры, называемые ведомыми, подключаются на входы запросов прерываний ведущего контроллера. На рис. 6.19 показано, как соединить пять контроллеров прерываний для того, чтобы обработать до 36 различных типов прерываний. Четыре прерывания от устройств подаются в ведущей контроллер, а остальные 32 прерывания вначале проходят через ведомый. Линии, соединяющие пять контроллеров прерываний с шиной процессора, на рисунке не показаны. Чтобы контроллеры прерываний действовали согласованно (см. далее), каждый из них должен знать свое предназначение. Сигнал на входе SP/EN сообщает контроллеру, является он ведущим или ведомым. Кроме того, во внутреннем регистре ведущего контроллера зафиксировано, какие входы запросов прерываний подсоединены к ведомым контроллерам, а какие непосредственно к устройствам. Во внутреннем регистре каждого ведомого контроллера хранится номер входа IR ведущего контроллера, к которому он (ведомый контроллер) подсоединен. са50 то cas2 |r1 cas3 ir2 Контроллер ir3 прерываний ir4 8259А j[g (ведомый) |r7 NT 5F/gR caso iro cas1 |r1 cas2 |r2 Контроллер ir3 прерываний ЙЙ 8259А № (ведомый) ir6 ir7 SftEN int iro ir1 ir2 caso cas1 cas2 Контроллер |r3 прерываний ir4 8259А jr5 (ведомый) jg^ NT caso cas1 cas2 Контроллер ir3 прерываний |r4 8259A jr5 (ведущий) Jj^ int sp/en На вход INTR процессора 80286 Рис. 6.19. Каскадное включение контроллеров прерываний 242
Предположим, что устройство запрашивает прерывание через один из ведомых контроллеров. Последний сформирует запрос прерывания через ведущий контроллер. Затем процессор 80286 отреагирует двумя циклами шины подтверждения прерывания INTA. В течение первого цикла шины INTA ведущий контроллер подтверждает запрос прерывания ведомого контроллера, выдавая его номер входа IR на линии каскадирования CAS. Эти линии соединяют ведущий контроллер со всеми ведомыми. Такое подтверждение необходимо потому, что запросить прерывание могут одновременно несколько ведомых контроллеров. Во втором цикле шины INTA выбранный ведомый контроллер помещает номер прерывания прерывающего устройства на шину данных, чтобы его мог считать процессор. После этого процессор реагирует вызовом соответствующей процедуры прерывания, как и в случае одного контроллера прерываний. Особенности работы. Микросхемы 8254 и 8259А были разработаны до появления процессора 80286. К сожалению, они не могут работать с частотой синхронизации 8 МГц (системная синхронизация 16 МГц). Для обеспечения надежной работы простых схем, показанных на рис. 6.17 и 6.18, необходимо уменьшить частоту системной синхронизации до 10 МГц. Но можно оставить частоту системной синхронизации 16 МГц, если встроить дополнительные схемы для введения состояний ожидания и задержек в те циклы шины, в которых активны микросхемы 8254 или 8259А. Такие схемы опубликовываны в литературе. Прямой доступ к памяти. Самый быстрый способ передачи данных между устройством ввода-вывода и памятью через процессор 80286 заключается в использовании команд INS и OUTS. Например, следующая программа считывает 2048 слов из устройства ввода-вывода (например, с дискового накопителя) в память: MOV DX,portnum ; portnum - это номер устройства MOV СХ,2048 ; 2048 слов = 4096 байт LES DI,memva ; memva - это виртуальный адрес ; приемника REP INSW ; Выполнение операции считывания Каждое слово вначале считывается в процессор в цикле шины ввода, а затем записывается в память в цикле шины записи в память. Так как цикл шины длится два цикла процессора и для передачи требуются два цикла шины, скорость передачи составляет (при частоте синхронизации процессора8МГц): м/,й. , ч (8М тактов/с)(2 байта/слово) ■ ; = 4М байт/с. 4 такта/слово 243
Желательно передавать слово из устройства ввода-вывода в память за один цикл шины, а не за два. Скорость передачи данных составила бы 8М байт/с, что равно максимальной пропускной способности шины. Высокая пропускная способность особенно важна в системах виртуальной памяти, где необходимо передавать большие сегменты с диска и на диск. Для практической реализации поставленной задачи требуется прямой канал в память, не проходящий через процессор 80286. Именно для организации каналов прямого доступа к памяти (ПДП) в процессоре предусмотрен вход запроса шины HOLD и выход подтверждения запроса шины HLDA. Когда устройство формирует сигнал HOLD, процессор заканчивает текущий цикл шины, прекращает управление шиной и выдает сигнал HLDA. После этого устройство, выдавшее сигнал HOLD, может управлять шиной до снятия сигнала HOLD. Процессор 80286 полностью освобождает шину. Запросившее шину устройство несет ответственность за генерирование сигналов состояния шины, управление шиной адреса и распознавание запросов состояний ожидания. Таким образом, устройство может управлять шиной данных в циклах записи в память (прямая передача в память) и контролирует шину данных в циклах считывания из памяти (прямая передача из памяти). Очевидно, для полного управления %шиной потребуется очень сложная схема. Вместо введения такой схемы в каждое устройство, которое хочет осуществить ПДП, она сосредоточена в одной микросхеме 82258, называемой контроллером ПДП. Эта микросхема содержит четыре канала ПДП, поэтому может обслужить до четырех устройств. Назначение канала ПДП - образовать поток обращений к памяти. Именно канал отвечает за производство правильного обращения к памяти (считывание или запись) и в правильную ячейку. Подключенное к каналу устройство ввода-вывода отвечает только за выдачу своих данных на шину и прием данных с шины по сигналам канала. Канал ПДП генерирует требуемый поток обращений, выполняя канальную программу, которую он считывает из памяти (см. рис 6.20). Канальная программа представляет собой последовательность командных блоков. Каждый командный блок определяет операцию памяти (считывание, запись), реализуемую каналом ПДП. Последний канальный командный блок в канальной программе содержит команду STOP. Каждый командный блок содержит физические адреса источника и (или) приемника в передаче (обычно используется только один адрес) и число передаваемых байт. Следовательно, одна канальная программа может задать передачу с участием нескольких областей памяти. Такая возможность рассеивать входные данные по памяти и собирать данные 244
Командный / блок 1^ Командный^ блок 2 <4— 16 бит Команда 1 0 Источник 1 2 Получатель 1 6 Порядок - Счетчик 1 - 10 выполнения каналом командных Состояние 1 14 Команда 2 16 блоков - Источник 2 18 ■ Получатель 2 22 - Счетчик 2 26 Состояние 2 30 |\/S/V\/1 Последний командный блокЧ Команда останова Рис. 6.20. Канальная программа для вывода называется рассеивающим считыванием и собирающей записью. Мы не будем подробно обсуждать программирование канала, а сосредоточимся на аппаратном интерфейсе. Контроллер ПДП имеет несколько внутренних регистров, часть из которых дублируется для каждого из четырех каналов. Для инициирования передачи ПДП программа процессора 80286 должна вначале загрузить в регистр указателя команды нужного канала начальный адрес канальной программы. После этого программа оповещает подключенное к каналу устройство ввода-вывода о начале передачи. Выдав оповещение, процессор может заняться другими делами. Контроллер ПДП прервет процессор, когда канальная программа закончится. 245
От генератора синхронизации CLK RESET READY 82258 К шине адреса HOLD HLDA АО-А23 В процессор 80286 В устройства В-контроллер прерываний данных К шине Сигналы управления шиной / Для считывания и записи во внутренние регистры Рис. 6.21. Контроллер прямого доступа к памяти Микросхема 82258 показана на рис. 6.21. Она имеет сигналы DREQ (запрос ПДП), DACK (подтверждение ПДП) и EOD (конец ПДП) для каждого канала. Когда устройство ввода-вывода готово передавать слово, оно сигнализирует каналу по входу DREQ. Канал показывает свою готовность к передаче (т. е. к инициированию нужного цикла памяти) выходным сигналом DACK. После этого устройство считывает с шины данных при выводе или выдает данные на шину данных при вводе. Последовательные сигналы DREQ вызывают обращения к последовательным словам в памяти, как определено в канальной программе. Само устройство ввода-вывода не знает, куда в память помещаются его данные и откуда берутся выводимые данные. Адресацией управляет контроллер ПДП по команде канальной программы. Когда канальная программа достигает конца, контроллер ПДП запрашивает прерывание по соответствующему выходу EOD. На рис. 6.22 показано, как подключить контроллер ПДП к системе на базе процессора 80286. Ради простоты показан всего один канал, а генератор синхронизации и контроллер прерываний опущены. Мы уже рассмотрели сигнальные линии HOLD, HLDA, DREQ, DACK и EOD. Сигнал ВНЕ обсуждается в следующем разделе. Отметим, что линия HLDA запрещает приемопередатчики шины данных, когда процессор не управляет шиной. Когда контроллер ПДП получает управление шиной, он формирует сигналы состояния шины SO, SI и М/Ю и адреса А0 - А23. 246
MRDC MWTC Контроллер шины 82288 ALE DEN DT/R SO S1 м/Ю RD VMR Контроллер периферийного устройства WR1 f 1Г RD Команда считывания из памяти Команда записи в память Шина адреса ALS245 Рис. 6.22. Подключение контроллера ПДП 82258
Контроллер ПДП подключен так, что к его внутренним регистрам можно обращаться, применяя ввод-вывод, отображенный на память. Поэтому на его входы RD и WR поданы сигналы MRDC и MWTC. Для адресации внутренних регистров используются линии адреса А0 - А7. Остальные линии адреса с помощью программируемого компаратора формируют сигнал выбора кристала. Контроллер периферийного устройства (диска, ленты и т. п.) подключен к линиям DREQ и DACK, а также к шине данных. Сигналы MRDC и MWTC сообщают, является передача ППД вводом (записью в память) или выводом (считыванием из памяти). Для производства передач ПДП контроллер периферийного устройства не нужно подключать к шине адреса. Большинство периферийных контроллеров для реализации ПДП требуют обычного интерфейса ввода-вывода для команд (поиск, перемотка, запуск ввода-вывода и др.) и состояния (конец ленты, ошибка диска и т. п.). Этот интерфейс показан на рис 6.22 штриховыми линиями. 6.4. ДИНАМИЧЕСКИЕ ЗАПОМИНАЮЩИЕ УСТРОЙСТВА С ПРОИЗВОЛЬНОЙ ВЫБОРКОЙ Основная память компьютера, осуществляющая операцию записи, состоит из микросхем ЗУПВ. Имеются две разновидности ЗУПВ: статические ЗУПВ (СЗУПВ) и динамические ЗУПВ (ДЗУПВ). Каждый запоминающий элемент СЗУПВ сохраняет свои данные неопределенно долго, пока не выключается питание. К каждому запоминающему элементу ДЗУПВ необходимо обращаться минимум через 4 мс, иначе данные теряются. Здесь запоминающими элементами служат микроскопические конденсаторы, причем заряженный конденсатор означает хранение двоичной 1, а разряженный - двоичного 0. В процессе считывания/записи конденсатор подзаряжается, а при отсутствии обращений заряд исчезает. Следовательно, в динамических ЗУПВ требуется дополнительная управляющая схема для периодической регенерации каждого запоминающего элемента. Кроме того, динамические ЗУПВ оказываются медленнее статических. Это обстоятельство усложняет тот факт, что микросхема ДЗУПВ требует интервала задержки, называемого временем предзаряда, между последовательными обращениями, чтобы подзарядить различные конденсаторы, разряженные при обращении. Но, несмотря на все эти недостатки, в большинстве компьютеров в качестве основной памяти применяются динамические ЗУПВ, а не статические: их запоминающие элементы меньше и емкость микросхемы динамического ЗУПВ оказывается больше. Выигрыш в стоимости и емкости при использовании ДЗУПВ компенсирует расходы на дополнительные схемы. Запись отдельных байт. До рассмотрения вопросов подключения ДЗУПВ к процессору необходимо обсудить детали шины процессора. 248
Некоторые команды осуществляют считывание или запись одного байта, а не целого слова. Считывание не вызывает никаких трудностей, так как процессор может считать все слово и проигнорировать его половину. Однако при записи так делать нельзя, потому что передача слова в память исказит один байт, соседний с тем, который необходимо записать. Решение состоит в организации памяти в виде двух банков: один банк для всех байтов с нечетными адресами, а второй - с четными (рис. 6.23). При передаче байта с нечетным адресом процессор выдает специальный сигнал разрешения старшего байта ВНЕ. Рассмотрим возможные случаи передачи байт и слов с четными и нечетными адресами. Когда процессор передает байт с нечетным адресом, он формирует активный сигнал ВНЕ и помещает на шину нечетный адрес. При передаче же байта с четным адресом сигнал ВНЕ пассивен, а на шине находится четный адрес Передачи слов всегда включают в себя нечетный байт (а также и четный байт), поэтому сигнал ВНЕ активный. Кроме того, при передачах слов на шину всегда выдается четный адрес. Это означает, что нечетный банк должен участвовать в циклах шины, если сигнал ВНЕ активен, а четный банк - если адрес четный, т. е. младший бит А0 шины адреса содержите. Интерпретация сигналов ВНЕ и А0 показана в табл. 6.3. Таблица 6.3. Кодирование обращений к памяти ВНЕ Ао Операция 0 0 Обращение к четному байту 1 1 Обращение к нечетному байту 1 0 Обращение к четному слову 0 1 Никогда не возникает Отметим по рис. 6.23, что все байты с четными адресами передаются по линиям D0 - D7 шины данных, а все байты с нечетными адресами - по линиям D8 - D15 . Эти же правила применимы и к портам ввода-вывода. Вот почему в предыдущем разделе мы применяли только четные порты ввода-вывода и всегда подключали байтные периферийные устройства к линиям D0 - D7 . Устройство микросхемы ДЗУПВ. На рис 6.24 показана микросхема 51С256Н-12 динамического ЗУПВ. Она имеет организацию 256 К X 1 и вводит 18-битный адрес в два приема. Сначала на линии \ - \ помещаются 9 бит адреса. Они называются адресом строки и фиксируются в микросхеме стробом адреса строки RAS. Оставшиеся 9 бит адреса называются адресом столбца и для фиксации их с линии А0 - А8 предназначен строб адреса столбца CAS. Сигнал разрешения записи WE показывает выполняемую операцию считывания или записи. Записываемый бит берется с линии DIN, а считываемый бит появляется на выходе D0UT. 249
А0-А23 ВНЕ И A1' А23 В А1 " А23 Память байт Память байт с нечетными с четными адресами адресами Выбор Выбор ■■D8-D15 Шина адреса АО DC-D7 D0-D16 i Шина I данных Рис. 6.23. Адресации байт в памяти Адреса строки и столбца DOUT DIN А0-А8 RAS CAS WE ДЗУПВ 51C256H-12 Данные GAS" Рис. 6.24. Микросхема 51С256Н-12 динамического ЗУПВ 256К ГХНЯ гп 512-битная защелка 512 линий слов считывания 512 линий бит Рис. 6.25. Операция считывания в микросхеме динамического ЗУПВ 250
Заглянем внутрь микросхемы, чтобы разобраться, как она работает. На рис. 6.25 показано, что происходит внутри микросхемы ДЗУПВ во время операции считывания. Запись производится аналогично. Запоминающие элементы организованы в матрицу 512 х 512. Адрес строки выбирает строку (или слово) этой матрицы, а адрес столбца выбирает столбец. Кроме матрицы запоминающих элементов в микросхеме есть защелка для хранения адреса строки, защелка для хранения адреса столбца и "огромная" защелка на 512 бит для хранения содержимого целой строки. Цикл памяти начинается, когда сигнал RAS становится активным. Он стробирует адрес строки в его защелку. Адрес строки дешифрируется и выбирает одну из 512 линий слов. Все 512 запоминающих элементов строки опрашиваются одновременно по линиям бит и фиксируются в защелке. Опрос элементов разрушает их содержимое, поэтому микросхема восстанавливает строку из защелки. Пока все это происходит, сигнал CAS стробирует адрес столбца в его защелку. Затем адрес столбца дешифрируется и выбирает один бит зафиксированной строки, который появляется на выходе D0UT. Цикл памяти заканчивается, когда сигнал RAS становится пассивным. После этого начинается временной интервал для предзаряда. Если сигнал WE в середине цикла памяти изменяется с пассивного на активный (т. е. со считывания на запись), входной бит DIN заменит адресуемый бит в защелке строки. Этот новый бит заменит также старый бит в матрице, когда ее строка восстанавливается из защелки строки. Такой цикл называется циклом считывания/модификации/записи. Если в микросхему периодически стробируются адреса строк, но сигнал CAS остается пассивным, то получается регенерация адресуемых строк без изменения их содержимого и без выдачи выходного сигнала. Следовательно, для устранения потери данных потребуется всего 512 операций памяти, а не 262144. Контроллер ДЗУПВ 8207. Как видно из предыдущего материала, между интерфейсами микросхем ДЗУПВ и шины процессора имеются существенные различия. Согласование интерфейсов осуществляет микросхема 8207 контроллера ДЗУПВ. Кроме стробирования адресов строк и столбцов и периодической регенерации ДЗУПВ, контроллер реализует так называемое расслоение, которое эффективно скрывает время предзаряда. Контроллер ДЗУПВ организует память в четыре банка. Банк определяется битами А1 и А2 физического адреса. Поэтому при обращении к последовательности слов, хранимых в памяти одно за другим, происходит выбор банков: Банк 0, Банк 1, Банк 2, Банк 3, Банк 0, Банк 1, Банк 2,. . . 251
Так как время предзаряда микросхемы 51С256Н-12 составляет 70 не, что меньше одного цикла шины, можно игнорировать предзаряд, если процессор не обращается к одному и тому же банку два раза подряд. В этом случае контроллер ДЗУПВ автоматически сигнализирует процессору о введении состояния ожидания. Измерения показали, что это происходит довольно редко, так как в большинстве своем обращения к памяти оказываются последовательными. На рис. 6.26 показано, как подключить контроллер ДЗУПВ к микросхемам ЗУПВ. Они организованы в четыре банка, причем каждый банк содержит 256К слов (512К байт). Общая емкость памяти равна 2М байт. Каждый банк разделен на две половины, первая из которых содержит все байты с нечетными адресами, а вторая - с четными. Адреса с шины процессора 80286 подаются в контроллер ДЗУПВ. Биты А^ и А2 подключаются на входы выбора банка BS0 и BSj. Биты Аз - Ах х становятся адресом строки ДЗУПВ, а биты А12 - А2о - адресом столбца. Массив микросхем 51С256Н-12 Выбор на основе сигналов АО и ВНЕ Рис. 6.26. Подключение контроллера ДЗУПВ 8207 к массиву микросхем ДЗУПВ 256К 252
Подключение контроллера ДЗУПВ к шине. Показанную на. рис. 6.26 подсистему памяти можно использовать с процессором 80286, работающим на частоте 8 МГц, без введения состояний ожидания. Исключения составляют ситуации, когда происходят два обращения к банку подряд или когда запрос памяти приходится на операцию генерации. Подключение контроллера ДЗУПВ к процессору 80286 показано на рис. 6.27. Прежде всего отметим отсутствие на рисунке контроллера шины 82288: ради повышения быстродействия контроллер ДЗУПВ декодирует состояние шины непосредственно от процессора. Затем отметим отсутствие защелки адреса: для повышения быстродействия в контроллере ДЗУПВ предусмотрены свои защелки адреса. Однако сигналы AQ и ВНЕ, участвующие в адресации байт, не подаются в контроллер ДЗУПВ, поэтому для них требуются защелки. Здесь применяется 2-разрядная защелка ALS74, а не обычная 8-разрядная. Она стробируется сигналом LEN (разрешение защелки) от контроллера ДЗУПВ. Этот сигнал аналоги- Г енератор Рис. 6.27. Подключение динамического ЗУПВ к процессору 80286 253
чен сигналу ALE (разрешение защелки адреса) отсутствующего контроллера шины. Входной сигнал разрешения порта РЕ в контроллере действует как вход выбора кристалла, но он выбирает всю подсистему памяти, которой управляет контроллер. Выходной сигнал разрешения записи WE объединяется с информацией адресации байта, чтобы сформировать сигналы разрешения записи для четных и нечетных микросхем ДЗУПВ. На выходе подтверждения ААСК формируется сигнал не готов, если контроллер ДЗУПВ требует введения состояния ожидания. Отметим, что на схеме, показанной на рис. 6.27, имеются отдельные линии входных и выходных данных. Почему бы не соединить линии DIN и D0UT микросхем ДЗУПВ и не направить объединенные линии в процессор 80286? Этого делать нельзя, если мы хотим получить преимущества конвейерной шины процессора. Напомним, что процессор начинает выдавать адрес до запуска цикла шины. Поэтому при записи в память контроллер не выдает в микросхемы сигнал разрешения записи сразу после выдачи адресов строки и столбца. ДЗУПВ начинает операцию записи, которая прекращается только сигналом разрешения записи. Следовательно, на выходе D0UT временно появятся считанные данные и при объединении линий Dqut и DJN они будут взаимодействовать с данными, подлежащими записи. Решение заключается в том, чтобы заблокировать выход ДЗУПВ в ходе операции записи. Для этого можно использовать приемопередатчики, но лучше применить буферы AS240, обеспечивающие усиление и коммутацию в два раза быстрее обычных приемопередатчиков ALS245. Микросхема AS240 либо блокирует, либо усиливает свои входные сигналы в зависимости от сигнала разрешения выхода ОЕ. Контроллер ДЗУПВ формирует сигнал DBM, аналогичный сигналу DT/R управления направлением приемопередатчиков от отсутствующего контроллера шины 82288. Сигнал DBM можно использовать для управления выходным буфером после объединения его с сигналом ААСК для учета возможных состояний ожидания, что и сделано в схеме на рис. 6.27. Хотя на рис. 6.27 показана одна подсистема памяти 2М байт, можно образовать восемь таких подсистем с контроллерами ДЗУПВ прежде, чем будет достигнут 16М-байтный предел физического адреса. Конечно, ничто не препятствует дополнительному подключению контроллера шины, защелок и приемопередатчиков для ПЗУ и устройств ввода-вывода. Интерфейс ДЗУПВ наиболее сложен из рассмотренных нами интерфейсов. Но он же наиболее важен, поскольку процессор использует его наиболее интенсивно. Отметим, что размеры и стоимость показанной подсистемы памяти опеределяются микросхемами ЗУПВ, а не восемью дополнительными схемами интерфейса. 254
6.5. ИНТЕРФЕЙС ПРОЦЕССОРОВ 80286 И 80287 В гл. 5 рассматривался численный процессор (сопроцессор) 80287. Дадим краткий обзор его взаимодействия с процессором 80286. Оба процессора работают параллельно. Когда процессор 80286 встречает команду сопроцессора (команду ESC), он вначале проверяет, закончил ли сопроцессор предыдущую команду ESC Если сопроцессор не закончил ее, процессор 80286 будет ожидать, а затем известит сопроцессор о встреченной команде ESC. Сопроцессор начинает независимо выполнять команду ESC, а процессор 80286 переходит к следующей команде. Возможны и некоторые варианты такого взаимодействия, например при возникновении особых случаев или при выполнении некоторых административных команд, но мы привели типичную ситуацию. Взаимодействие процессоров. Чтобы выполнить указанные выше действия, процессоры должны обмениваться определенной информацией. Часть ее передается по шине процессора 80286, а другая часть - по специальным сигнальным линиям, соединяющим оба процессора. Ниже приведен список той информации, которую каждый процессор должен знать о другом, с описанием передачи необходимой информации. 1. Процессор 80286 должен знать, когда сопроцессор занят выполнением его предыдущей команды. Сопроцессор 80287 имеет выход занятости BUSY, сигнал на котором определяется состоянием бита В в его регистре состояния. Бит В равен 1, когда сопроцессор выполняет команду или сигнализирует прерывание, и В = 0, когда сопроцессор свободен. Для восприятия сигнала BUSY у процессора 80286 имеется вход BUSY. 2. Сопроцессор должен знать, что процессор 80286 встретил команду ESC, а также, что это за команда. Сопроцессор подключается к портам ввода-вывода F8 - FF на шине процессора 80286, которые применяются для взаимодействия процессоров. Когда процессор 80286 выполняет команду ESC, он извещает об этом сопроцессор, производя автоматический вывод в один из указанных портов. Такие автоматические операции ввода-вывода выполняются на шине как обычные операциии ввода-вывода. 3. Процессор 80287 должен знать адрес команды ESC, который нужен для регистра - указателя особого случая. Процессор 80286 передает эту информацию через порты, как описано в п. 2. Отметим, что в виртуальном режиме адрес команды должен быть виртуальным, а в реальном режиме достаточно физического адреса. 4. Если команда ESC имеет операнд в памяти, процессор 80287 должен знать адрес операнда, который запоминается в регистре - указателе особого случая. Этот адрес также передается через порты ввода-вывода. 255
5. Если команда ESC имеет операнд в памяти, то процессор 80287 должен как-то обратиться к нему. Обращение к операнду, занимающему несколько слов, требует преобразования адреса и контроля защиты. Процессор 80287 не обращается к операнду сам, а разрешает осуществить это процессору 80286, который и производит контроль защиты и преобразование адреса. Когда процессор 80286 обнаруживает в команде ESC операнд в памяти, он преобразует его адрес в физический адрес и значение предела, которые помещаются во внутренний регистр вместе с флажком направления, показывающим, должны данные передаваться в сопроцессор или из него. Оба процессора имеют линии запроса расширения процессора PEREQ. Эти линии, а также линии подтверждения расширения процессора РЕАСК, должны быть соединены друг с другом. Когда сопроцессору требуется считать или записать слово его операнда, он выдает сигнал PEREQ. После этого процессор 80286 подтверждает прием сигнала по линии РЕАСК и выполняет цикл шины памяти и цикл шины ввод-вывода в порты сопроцессора, чтобы реализовать требуемую передачу. После этого процессор производит инкремент внутреннего регистра, так что последовательные запросы PEREQ будут обращаться к последовательным словам в памяти и сопроцессор будет иметь доступ к операндам длиной в несколько слов. Схемы контроля проверяют внутренний регистр с целью обнаружения превышения сегмента. 6. Сопроцессор должен извещать процессор 80286 о возникновении незамаскированного численного особого случая. Оба процессора имеют линии ERROR, соединяющиеся друг с другом. На выход ERROR сопроцессора выводится состояние бита ES в регистре состояния, который показывает наличие незамаскированного особого случая. Процессор 80286 проверяет сигнал ERROR, когда он встречает команду ESC или WAIT, и при активном сигнале ERROR формирует особый случай сопроцессора. Подключение сопроцессора к процессору 80286. Схема подключения сопроцессора показана на рис. 6.28. Все линии связи подразделяются на три группы: межпроцессорные соединения, соединения порта ввода-вывода сопроцессора, подключения синхронизации сопроцессора. Мы уже рассмотрели основные межпроцессорные соединения - PEREQ, РЕАСК, ERROR и BUSY. Кроме того, сопроцессор контролирует состояние шины SO, SI, COD/INTA, READY и HLDA. Интерфейс порта ввода-вывода имеет несколько особенностей. Сопроцессор спроектирован с учетом прямого подключения к процессору 80286 без защелок и приемопередатчиков. Это сделано ради повышения 256
ШБ9 Г|внераторси( -p синхрони-§1 А зации 55 I 82284 ready cl* Контроллер шины 82288 den dt/B ale IOWC Юй5 ao-a15 reset read"y clk Процессор Fl 80286 fo do-d15| m/io PEREQl Ш5к\ coo/wt* hlda peack pereq соолкта reset read? clk286 Ц d1so0 Процессор 80287 nps1 cmd1 cmd0 clk скм Адрес fllipilBllllffil ао цаэ-а15 р1 р2 р0 pw»15 Программируемый компаратор ALS526 Z га а1 С ) D D с Заи 1елка AU 5175 и с > и Данные f1 df I _> Приемопередатчик ALS245 jr-i Генератор t ^ О ! синхрони- | -о' "СJ зации 8284А i L-- } Vc<r~ Рис. 6.28. Подключение процессора 80287 к процессору 80286 быстродействия, а также потому, что в некоторых системах защелки адреса и приемопередатчики данных не применяются. Поэтому для портов ввода-вывода сопроцессора нужна отдельная защелка для сигнала выбора кристалла NPS1 и бит адреса CMD0 и CMD1 (весь адрес фиксировать не нужно). В 4-битной защелке ALS175 используются только три бита. Так как порты ввода-вывода сопроцессора находятся по отношению к шинным приемопередатчикам на стороне процессора 80286, а не на стороне остальных устройств ввода-вывода, было бы неправильно использовать сигнал направления DT/R при выполнении ввода-вывода в сопроцессор. Поэтому сигнал выбора кристалла NPS1 запрещает шинные приемопередатчики, когда процессор 80286 производит операции ввода-вывода с портами сопроцессора. Однако нужно осторожно использовать сигнал выбора кристалла для этой цели. Не следует запрещать приемопередатчики в циклах шины памяти или прерывания, в которых случайно участвуют адреса или 257 9 Заказ 6126
номера прерываний, идентичные номерам портов сопроцессора. По этой причине сигналы М/Ю и COD/INTA подаются в схему дешифрирования адреса для сопроцессора, чтобы ограничить выбор кристалла сопроцессора только циклами шины ввода-вывода. Коснемся сигналов синхронизации сопроцессора. Процессор 80286 получает свой сигнал синхронизации CLK из системной синхронизации. Системная синхронизация подается также на вход CLK286 сопроцессора; этот сигнал используется сопроцессором в его взаимодействии с процессором 80286, но сопроцессор не привлекает его для внутренней синхронизации. Для этого предназначен вход CLK; следовательно, оба процессора работают со своим быстродействием. Вход CLK сопроцессора можно подключить к отдельному генератору синхронизации, но можно соединить его с той же системной синхронизацией, что и у процессора 80286. Если на вход CLK подается системная синхронизация, вход СКМ должен быть заземлен. При этом сопроцессор делит значение частоты системной синхронизации на три. Если, например, частота системной синхронизации составляет 16 МГц, то процессор 80286 будет работать на частоте 8 МГц, а сопроцессор - на частоте 5 3 МГц. Чтобы процессор работал быстрее, требуется отдельный генератор синхронизации. Если, например, подать сигнал 8 МГц на вход CLK, то сопроцессор будет работать на частоте 8 МГц (это его максимальная частота). Чтобы избежать при этом деления частоты, вход СКМ следует подключить к источнику +5 В. Напомним, что генератор синхронизации 82284, выдавая сигнал системной синхронизации 16 МГц, также формирует и сигнал PCLK с частотой 8 МГц. Было бы хорошо подать сигнал PCLK на вход CLK сопроцессора и убрать лишний генератор синхронизации. Однако для сопроцессора требуется специальный сигнал синхронизации с коэффициентом заполнения 1/3. Он вырабатывает такой сигнал из системной синхронизации, когда делит значение частоты на три. Но когда входной сигнал СКМ равен 1, сопроцессор воспринимает сигнал CLK как есть, поэтому сигнал PCLK с коэффициентом заполнения 1/2 не подходит. Для получения специального сигнала синхронизации 8 МГц для сопроцессора требуется генератор синхронизации 8284А. ЗАКЛЮЧЕНИЕ На этом мы заканчиваем рассмотрение процессоров 80286 и 80287. Мы показали их внутреннюю организацию и изучили их системы команд. Кроме того, мы познакомили с такими возможностями процессоров, как управление памятью, защита и мультизадачность. Наконец, мы обсудили, как объединить оба процессора и другие компоненты в законченный 258
компьютер. Полученная система обладает такой же мощи^стю, как наиболее мощные современные миникомпьютеры и какой обладали крупные компьютеры всего несколько лет назад. Появление процессоров 80286 - 80287 является заметной вехой в эволюции микропроцессоров. Между функциональными возможностями микрокомпьютера и крупных компьютеров остается лишь небольшой промежуток. Можно с уверенностью предположить, что в ходе своего развития микропроцессор догонит крупный компьютер.
ПРИЛОЖЕНИЕ А СИСТЕМА КОМАНД ПРОЦЕССОРА 80286 АЛ. ПРИМЕЧАНИЯ О ВРЕМЕНИ ВЫПОЛНЕНИЯ КОМАНД Приводимые далее времена выполнения команд (в тактах синхронизации) определяют максимальную производительность процессора 80286. Без задержек в циклах шины фактическое время выполнения программы (в тактах синхронизации) в среднем будет на 5 % больше вычисленного времени выполнения из-за таких последовательностей команд, которые выполняются быстрее, чем они могут быть выбраны из памяти. Для вычисления собственно времени выполнения последовательностей команд следует умножить суммарное число тактов синхронизации на период сигналов синхронизации. При частоте синхронизации процессора 8 МГц период составляет 125 не; на входе системной синхронизации CLK процессора частота сигналов должна быть 16 МГц. А.2. ПРЕДПОЛОЖЕНИЯ О ВРЕМЕНИ ВЫПОЛНЕНИЯ КОМАНД 1. Команда предварительно выбрана из памяти, дешифрирована и готова к исполнению. Число тактов синхронизации команды передачи управления включает в себя все время, необходимое для выборки, дешифрирования и подготовки к выполнению следующей команды. 2. Циклы шины не требуют состояний ожидания. 3. Нет передач данных сопроцессора 80287 и запросов HOLD локальной шины. 4. При выполнении команды не возникает особых случаев. А.З. ПРИМЕЧАНИЯ ПО СИСТЕМЕ КОМАНД Адресные смещения, определяемые полем MOD, не показаны. При необходимости они размещаются за показанными полями команд. Выше/ниже относится к беззнаковым значениям. Больше относится к более положительному знаковому значению. Меньше относится к менее положительному (более отрицательному) знаковому значению. Если d = 1, то в регистр; если d = 0, то из регистра. Если w -1, команда оперирует словами; если w = 0 — байтами. Если s - 0, то операнд образован 16-битными непосредственными данными. Если s = 1, то для образования 16-битного операнда непосредственный байт данных расширяется со знаком. Символ х обозначает неопределенное двоичное состояние. Символ z используется в цепочечных примитивах для сравнения с флажком ZF. Если показаны два числа тактов синхронизации, то меньшее относится к операнду в регистре, а большее — к операнду в памяти. Звездочка (*) показывает необходимость добавления одного такта, если вычисление смещения требует суммирования трех элементов. Символ п обозначает число повторений. Символ т показывает число байт в следующей команде. Уровень L обозначает лексический уровень вложения процедуры. 260
Система команд процессора 80286 Функция Формат Число тактов Передачи данных MOV = передать: __________ Регистр в регистр/память И ООО 1 00w jmod reg r/m] Регистр/память в регистр |1 ООО 1 0 1 w |mod reg r/m| Непосредственный мдЛп< « i iAnn>, операнд в регистр/ память И 1 ООО 1 1 w |mod ООО r/m Непосредственный операнд!« A i i ц» ran в регистр м| I и 11 w reg Память в аккумулятор Аккумулятор в память Регистр/память в сегментный регистр Сегментный регистр в регистр/память PUSH = включить в стек: Память Регистр Сегментный регистр Непосредственный операнд [о 1 1 0 10s0 I data | data if s=0 1 111111111 [mod 1 10 Т7гт 101010 regl I000 reg; 1 1 ol PUSHA = включить в стек все POP * извлечь из стека: В память В регистр В сегментный регистр 101 100000 I И 0001 1 1 1 [modООО 77т 10101 1 гедП IQOOregl 1 1 I (reg #01) РОРА = все извлечь из стека 101100001 XCHG = обменять: ______ Регистр/память с регистром] i Q00Q 1 1 w |mod reg r/m I - ввести: . . фиксированного порта [IJHOOJjDwl Port l П1101 10wl И 1 1001 1 w I port | Регистр с аккумулятором IN Из Из переменного порта OUT = вывести: В фиксированный порт В переменный порт XLAT = преобразовать байт из Al LEA = загрузить ЕА в 100 10 reg 111 101 iTwl ffl 0 1 0 1 1 1 I 110001 101 jmod reg flm\ LDS Загрузить указатель И 1000 101 Imodreg r/m| (mod Ф 11) LE°SS= загрузить указатель •ЩЩЩЫШ^ <™d * 11> .А^загрузитьАН ^"ощщ из флажков Л 001 1 1 1 0 I &=ахаП°МНИТЬ АН В° J1001 1 100 I PUSHF = включить в стек il001 i 101 i флажки Axyj i POPF - извлечь из стека во фЛаЖКИ _._______„_»_«_»_«■.■_■■■-- 2,3* 2,3* 2,5* 2,5* 2,3* 2,3* 2 2 5 5 3 3 2,5* 17,19* 2,3*' 2,3* 5* 5* 3 3 3 3 3 3 17 17 5* 5* 5 5 5 20 19 19 3,5* 3,5* 3 3 5 5 5 5 3 3 3 3 5 5 3* 3* 7* 21* 7* 21* 2 2 2 2 3 3 5 5 261
Система команд процессора 80286 (продолжение) Функция Формат Арифметические ADD - сложить: Регистр/память с регист- 1000ОООd wlmod reg r/m| ром, в любой I. . . . л ^=^==^=^==4 Непосредственный операнде 0 0 00 0 s w |mod ООО r/m data Число тактов 'ежим реального адреса Ражим защищ. вирт. адреса 2,7* 2,7* 37* 3,7* 3 3 2,7* 2,7* 3,7* 3,7* 3 3 2,7* 2,7* 2 2 2,7* 2,7* 3,7* 3,7* 3 3 2,7* 2,7* 3,7* 3.7* 3 3 2,7* 2,7* 2 2 2,6* 2,6* 2,7* 2,7* 3,6* 3,6* 3 3 2 7* 3 3 3 3 3 3 3 3 13 13 21 21 16* 16* 24* 24* 13 13 21 21 16* 16* 24* 24* 21,24* 21,24* 14 22 17* 25* 14 22 17* 25* /-гйг^-аст«ц^,.1ооооб i owi-as- jdataif s w=01 с аккумулятором ADC 3сложить с переносом: Регистр/память с регистром, в любой 1 data if w=T| data fclata if s w=01 ^посредственный операнд|1 QQOOOsw jmod01 0 r/m с регистром/памятью !ЛЛЛ. n .^г==т====т=Г====т . i Непосредственный опеоандЮОО » 01 иwl data I data if w= 11 с аккумулятором INC - инкремент: . Регистра/памяти |1 1 1 1 1 1 1w rnodOOO r/m Регистра 101000 гесП SUB = вычесть: Регистр/память из регистра,! ■ ■ в любой |001010dw |mod reg r/m| Непосредственный операнд из регистра/памяти Непосредственный операнд из аккумулятора SBB = вычесть с заемом Регистр/память из регистра, в любой |1 00000sw |mod 101 r/m| data Wataif sw=01l |0010110w| data I data if w = T| 1000 1 1 Odw|mod reg r/m| 1100000sw |mod01 1 r/m data data if s w=01 data из аккумулятора DEC = декремент: _ Регистра/памяти И 1 1 1 1 1 1 w |mod 0 0 1 r/m| Регистра |01001 reg"1 CMP = сравнить: Регистр/память с регистром|00 1 1 1 Q? w jmod reg r/m| Регистре регистром/па- |р0п , 00w|mod reg r/m| 00000s w Imod 1 1 1 r/ml data data if s w-01 й'аЕ^ЯКЙЙ °ПеРаНА l°Q11110wl d_j!f !dataifw=Tl NEG = изменить знак И 1 1 1 0 1 1 w jmod 0 1 1 r7m\ AAA = коррекция ASCI I fe====i 1 для сложения 100110111 I DAA = десятичная коррек- L , « ~ ~ „ < „ ция для сложения 100 1001 1 1 AAS = коррекция ASCI I Гл^^^ТТ для вычитания |QO 1 1 i 1 1 1 DAS = десятичная коррек- inn i n 1 1 1 1 I ция для вычитания |OU i О i 1 1JJ MUL-умножить(без знака)^1 11101 1 wImod ЮОг/ггД Регистр-байт Регистр-слово Память-байт Память-слово IMUL = умножить (со знаком) : Регистр-байт Регистр-слово Память-байт Память-слово IMUL = умножить на непо- з51ком)еННЬ,Й °ПеРаНД <С° 101 101 0s 1 Imod reg r/m| datT DIV = разделить(без знака)^ i i i p т i w |mod 1 l"or7m1 Регистр-байт 1 1 —J Регистр-слово Память байт Память-слово И 1 1 1 01 1 w [mod 101 r/m| I data if s = 0 I 262
Система команд процессора 80286 (продолжение) Функция Формат Режим реального адреса Число 1 адреса 11 1010100 1000010 нЛ И1010Ю1100001 о ПП 11001 10001 Арифметические (продолжение) _______ IDIV = разделить(сознаком)|1 111011 w|mod 1 1 1 r/m| Регистр-байт Регистр-слово Память-байт Память-слово ААМ = коррекция ASCII для умножения AAD = коррекция ASCI I для деления SBW = преобразовать 1ЙТ в слово _ CWD = преобразовать слово 11001 1001 1 в двойное слово 1 1 Логические Команды сдвига/циклического сдвига Регистр/память на 1 и 1 0 1000 w imod ТТТ r/m| Регистр/память на CL |1 101001 w|mod ТТТ77ml Регистр/память на счетчик |1 1 00000 w imod ттттттг ТТТ Команда 000 00 1 0 1 0 0 1 1 1 00 1 0 1 AND = объединить по И: 111 КР™е*йТЬСРеГИСТ- l0010UOdw|modre9-r7ml ?ЯетаНМЬ£ь°юераНД hmofloflwlmodl-Г-д Непосредственный операнд |00 1 00 1 0 w| с аккумулятором 1 1 TEST = функция с результатом во флажках , - , . , Регистр/память с регистром! 1 00001 0 w|mod reg r/m| 17 25 20* 28* 16 14 2,7* 5+л,8Чл 17 25 20* 28* 16 14 2 2 2,7* ROL ROR RCL RCR SHL/SAL SHR SAR data data i data if w* ldata if w = 11 3 ?еЯдаНаНм^ьюеРвНД InHOHwImodOdbr/ml data ldata it w П I clK°K^y^eop!jbi? операнд HOIOIOOwl data ldata if w°1| OR - объединить no ИЛИ: ро^в7"C РеГИСТ- IO00O lOdwImodreo-ra Непосредственный операнд Ц 000000 w jmod 00 l77mT с регистром/памятью Непосредственный операнд lOQQO 1 10w| с аккумулятором XOR — Объединить по иск лючающему-И Л И: Регистр/память с регист- Ю01 1 00 d w|mod reg 7Jm\ ром, в любой у \ 8 ' I Непосредственный операнд И 000000 wlmod 1 10 r/m] с регистром/памятью " r/m| data ldata if w = 11 data i data if w = 11 Непосредственный операнд I001l010w| data ldata if w= 11 с аккумулятором « лч ■— ■ NOT « инвертировать И 1 1 1 011 w imod 0 10 r/ml регистр/память Цепочечные команды MOVS = передать байт/слово! 1 0 1 00 10 w i CMPSB/W = сравнить байт/ ьП1ПЛ,, слово И 0 100 1 1 w i ISA|S=CKaHMP0BaTb баЙТ/ И 0 10 11 1 w ■ LODS = загрузить байт/сло- f во в AL/AX | STOS * запомнить байт/сло- И 01 0 10 1 w i во из AL/AX ) | INS = ввести байт/слово из |0 1 101 1 0 w | порта, адресуемого DX 5==г===г=5_{ OUTS = вывести байт/словою 110111 w i в порт, адресуемый DX data ldata if w-"TI Л'Н'ЩП 2.7* 3.7* 3 2,6* 3,6* 3 2,7* 3,7* 3 2,7* 3,7* 3 2,7* 5 8 7 5 3 5 5 2,7* 3,7* 3 2,6* 3,6* 3 2,7* 3,7* 3 2,7* 3,7* 3 2,7* 5 8 7 5 3 5 5 263
Система команд процессора 80288 (продолжение) Число тактов Ражим реального адреса Режим защищ. вирт. адреса 5+4п 5+4п 5+9п 5+9п 5+8п 5+8п 5+4п 5+4п 4+Зп 4+Зп 5+4п 5+4п 5+4п 5+4п 7 + т 7+т Функция Формат Цепочечные команды (продолжение) Повторение со счетчиком из СХ MOVS«передать цепочку М 1 1 1 001 0 110 100 10 w| CMPS «сравнить цепочку |Щ iQOiz |lQ10011w| SCAS»сканировать цепочкурп" 11601z |l 0 10 1 1 1 wl LODS■ загрузить цепочку II 1 1 10010 110101 10wl STOS-запомнить цепочку И 1 1 10010 11010101 wl INS«ввести цепочку И 1 1 10010 101 101 l"5w| OUTS = вывести цепочку И 1 1 10010 101 101 iTwl Передачи управления CALL «вызов: Прямой внутри сегмента П 1 101000 I displow I disp-high | Косвенный через регистр/ iMI111ii U^ft4flr/ml память внутри сегмента II 1 1 1 ИМ Imod 0 1 0 r/m) Прямой межсегментный 110 0 1 10 1 0 | Смещение сегмента "1 Г" Селектор сегмента | Только защищенный режим (прямой внутрисегментный): Через шлюз вызова на том же уровне привилегий Через шлюз вызова на другом уровне привилегий, без параметров Через шлюз вызова на другом уровне привилегий, с параметрами Через TSS Через шлюз задачи Косвенный межсегментный И 1 1 М 1 1 1 Imod 01 1 r/m| (mod * 11) Только защищенный режим (косвенный межсегментный): Через шлюз вызова на том же уровне привилегий Через шлюз вызова на другом уровне привилегий, без параметров Через шлюз вызова на другом уровне привилегий, с параметрами Через TSS Чеоез шлюз задачи JMP = безусловный переход: Короткий/длинный И 1 10101 1 I disptow I 111101001 I displow I disphigh | память внутри сегмента Прямой межсегментный Прямой внутри сегмента ^тГвЬнЖгЖР/ •|"1"ЧЧ™*"00Ж 111101010 | Смещение сегмента ~| I Селектор сегмента | Только защищенный режим (прямой межсегментный): Через шлюз вызова на том же уровне привилегий Через TSS Через шлюз задачи Косвенный межсегментный И 1 1 1 1 11 1 Imod 101 г/mj (mod =£11) Только защищенный режим (косвенный межсегментный): Через шлюз вызова на том же уровне привилегий Через TSS Через шлюз задачи RET ■ возврат из i Внутри сегмента Внутри сегмента с прибав- |110QQ010| data-low I data high! пением константы к SP 1 1 1 -~- Межсегментный Межсегментный с прибавле-| нием константы к SP Только защищенный режим (RET): На другой уровень привилегий И 100001 Г 11 100101 1 I 7+m,11+m* 7+m,11+m 13+m 26+m 41+m 82+m 36+4x+m" " 177+m" 182+m 16+m 7+m 7+m 7+m,11+m 29+m* 44+m* 83+m* )0+4х+пг 180+m< 185+m1 7+m 7+m 11+m 23+m 38+m 175+m 180+m 15+m* 26+m* 41+m* 178+m* 183+m* 11+m 11+m 11+m 11+m 15+m 25+m 15+m 55+m 264
Система команд процессора 80286 (продолжение) Функция Формат Число тактов вирт. Передачи управления (продолжение): ^&уел'ьейти'еспи 'loinoiod I disp ,1-ШОЕ=перейти, если меньГоТТТТТОоТ dTso" ше/не больше или равно ' ' uiaw JLE/JNG = перейти, если не 101111110 I disp меньше или равно/не больше-- 1 JB/JNAE = перейти, если ни-|р 1 1 1QQ1Q I disp же/не выше или равно ■ I г JBE/JNA = перейти, если |01110110i disp ниже или равно/не выше \. л т л л ^ л л ■ JP/JPE = перейти, если есть 101111010 I disp паритет/паритет четный 101 1 10000 I d^o" JO - перейти, если есть пере»Ц-1- |цици °'sp полнение JS = перейти.если есть знак ^01 1 1 1000 I disp' JNE/JNZ = перейти, если не 101 1 10101 I равно/не нуль ' 1 disp JNL/JGE = перейти, если не 101111101 | disp меньше/больше или равно л<4<<4<< .. JNLE/JG = перейти, если не 101111111 I disp меньше или равно/не большеn iiinn11 I ai*,* JNB/JAE = перейти, если не1 100 11 1 d>sp ниже/выше или равно J n 1 1 1 Г) 1 1 1 I disn J N BE/J А = перейти, если не 1 1 1и 1 1 1 S? ниже или равно/выше J0 1 1 1 1 0 1 1 | disp JNP/JPO = перейти, если нет , _ 4 , „ л л л т "Г" паритета/паритет нечетный J0 1 1 1 000 1 I disp J NO = перейти, если нет Х|П1111ЛП1 i п переполнения 101 1 1 1001 1 disp_ М$=перейти,если нет знакаJl 1 100010 I disp LOOP = заииклить СХ раз *Л 1 г LOOP = зациклить СХ раз ^\ \ г LOOPZ/LOOPE = зациклить,Ц 1 Ю0001 1 disp пока нуль/равно , ' LOOPNZ/LOOPNE = зацик- II 1 100000 I disp лить, пока не нуль/не равно i л 1 1 nnn 1 1 i -7-- 1 JCXZ = перейти, если СХ ра-11 1 10001 1 I --2Е 1 вен нулю ENTER = войти в процедуру|, j 0Q1 0Q0 | data.tow | data-high | L = 1 L>1 I 1 .~_w* ~ 11 1001001 LEAVE = выйти из процедуры INT = прерывание: Указанного типа 111001101 I type | Типа 3 И 1001 100 I INTO = прерывание по nepe-i« i по 1 110 I полнению I' I Только защищенный режим: Через шлюз прерывания или специального прерывания на том же уровне г Через шлюз прерывания или специального прерывания на другом уровне г Через шлюз задачи IRET ния 7+mor3 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 7+тогЗ 8+тог4 8+тог4 В+тог4 8+тог4 8+тог4 8+тог4 8+тог4 8+тог4 11 15 &+4(L-1) 5 23+т 23+т 11 15 16+4(1.-1) 5 ; возврат из прерыва- 111001111 I ^Только защищенный режим: На другом уровне привилегий В другую задачу т (NT = 1) BOUND = обнаружить значе-JQ 1 1 ООО 1 0 |mod reg r/m] ние вне диапазона 24+гтюгЗ (3 если нет | прерывания) 1 40+m л 78+m 167+m 17+т 31+m 55+m 169+m 13* 13* Использ. INTT числам тактов синхр.. если] | особый случай 5) I 265
Система команд процессора 80286 (продолжение) Функция Число тактов реального адреса Управление процессором CLC — сбросить перенос CMC-инвертировать переносу? 1 10101 11 1 1 1 1000 STC — установить перенос И 1 1 1 1 ОРТ CLD - сбросить направление 1J1J1J00 8TD ■ установить направлен i i 1 1 1 1 ОТ" CLI- сбросить прерывание 111 1 1 10ПТ ЭТИустановить прерывание 11111011 Н LT - останов 1111101ОО" WAIT * ожидать 11001 101 1 LOCK-префикс блокиров-i, 1ППППП ки шины v |1 1 1 10 ОСТР CLTS - сбросить флажок переключения задач 00001 1 1 1 1000001 1 (Л 11101 1 ТТТ Imod LU>f7m| ТТТ LLL - КОП для процессора расширения ESC* переключиться на сопроцессор SEG «префикс замены сег-гг—-— мента |001 reg 110f Управление защитой Ж„:Ж"«. loooon пГоооооо— SGDT - запомнить регистр 100001 1 1 1 1000 00001 глобальной дескрипт. табл.^= = ■ jivwajiDnvvi деычртш •« I ДОЛ.■——*—*—~~~** I LIDT - загрузить регистр 100001 1 1 1 100000001 дескрипт. табл. прерываний! |ялллллп< SIDT-запомнить регистр 100001 1 1 1 100000001 дескрипт. табл. прерываний LLDT - загрузить регистр^ J00001 1 1 1 100000000 локальной дескрипторной" локальной дескрипторной™""* slfpT^Uno^HHTb^fficTp^e00011 11 lOOOOOOOO птооной локальной дескрипторной ^ЛЛЛЛ. _ , _ i_ . . _ ^ _ _ _ таблицы в регистре/памяти 100001 1 1 1 100000000 LTR« загрузить регистр А..., , . задачи из регистра/памяти 100001 1 1 1 100000000 STR - запомнить регистр / _ задачи в регистре/памяти ' irjOOOl 1 1 1100000001 LMSW - загрузить слово cocv7 u u ' ' 1 »uuuuu машины из регистра^амятирооо! у у IIQOOOOQOT fiMSW в запомнить СЛОВО 1 1 SMSW - запомнить слово состояния машины ЗЙ-ТгруСправа до- J0000 11 1 1 1000000 10" ггтмпл м-а гмагмгтпа/памяти ^ ггупа из регистра/памяти , , LSL - загрузить предел сег- iQQQQl 11 ЦООООООТТ г_гмгтпя/пя_яти 1 ' 1 ' мента из регистра/памяти ARPL - скорректировать запрошенный AHPL = скорректировать запрошенный п - 1ППГ) 1 1 уровень привилегии из регистра/памяти Iи 1 1 и u и ' jl У ERR « проверить права 100001 1 11 100000000 проверить права доступа: регистр/память VERW = проверить доступ по записи [mod 0 1 0r/m| mod 00 Or/ml Imod 0 1 1 r/ml [mod 00 1 r7m] [mod 0 1 0 r/ml mod 000 r7m| [mod 0 1 1 r/ml mod 00 1 r/m| mod 1 1 0 rTrnl [mod 100 r7m] r/mj mod reg r/m I mod reg r/m | mod 100 r/ml 100001 1 1 1 100000000 Imod 101 r/ml 2 2 2 2 2 3 2 2 3 0 2 9-20* 11* 11* 12* 12* 3,6* 2,3* 266
Байт 1 Байт 2 7654321076543210 Байт 3 Байт 4 Байт 5 Байт 6 1 КС )П d w ГШ od '•9 r/m Г т г • Младшие | Старшие I энные 1 Старшие данные ' смещение/данные | смещение/данные I ладшие да | | _ _ J. _ _ _ J Регистр-операнд/регистры, используемые в вычислении смещения Регистр-операнд/расширение кода операции Регистровый режим/режим памяти с длиной смещения Операция с байтом/словом Направление в регистр/направление из регистра Код операции а) 7 Байт 1 Байт 2 6543210765432 1 0 7 6 Байт 3 5 4 3 2 1 0 т IIII11II 1 1 1 1 1 Длинный КОП 1 mod '•fl r/m Байт 4 Байт 5 Младшее смещение j Старшее смещение J Рис. АЛ. Примеры форматов команд процессора 80286: о — формат с коротким кодом операции, б — формат с длинным кодом операции
А.4. ОБОЗНАЧЕНИЯ Эффективный адрес ЕА операнда в памяти вычисляется в соответствии с полями mod и г/т: если mod ■ 11, то г/т интерпретируется так же, как поле REG, если mod = 00, то DISP = 0 (за исключением ситуации: если mod = 00 и г/т = ПО, то ЕА = = disp-high: disp-low); disp-high и disp-low отсутствуют, если mod = 01, то DISP = disp-low с расширением со знаком до 16 бит, a disp-high отсутствует, если mod = 10, то DISP = disp-high: disp-low, если г/т = 000, то ЕА = (ВХ) + (SI) + DISP, если г/т = 001, то ЕА = (ВХ) + (DI) + DISP, если г/т - 010, то ЕА = (BP) + (SI) + DISP, если г/т = 011, то ЕА - (BP) + (DI) + DISP, если г/т = 100, то ЕА = (SI) + DISP, если г/т = 101, то ЕА = (DI) + DISP, если г/т = ПО, то ЕА - (BP) + DISP, если г/т = 111, то ЕА = (ВХ) + DISP, DISP находится после второго байта команды (перед данными, если они требуются в команде). А.5. ПРЕФИКС ЗАМЕНЫ СЕГМЕНТА 001 reg 110, где reg кодируется следующим образом: reg Сегментный регистр 00 ES 01 CS 10 SS 11 DS Кодирование поля REG: REG 16 бит (w = 1) 8 бит (w = 0) 000 АХ AL 001 СХ CL 010 DX DL 011 ВХ BL 100 SP AH 101 BP CH ПО SI DH 111 DI BH Физические адреса всех операндов, адресуемых через регистр BP, вычисляются с привлечением регистра SS. Физические адреса операндов-приемников (адресуемых регистром DI) в цепочечных примитивах вычисляются с привлечением регистра ES, который нельзя заменять. 268
ПРИЛОЖЕНИЕ Б СИСТЕМА КОМАНД ПРОЦЕССОРА 80287 Система команд процессора 80287 Передачи данных FLD = загрузить Целое/вещественное из памяти в ST (0) Длинное целое из памяти в ST(0) Временное вещественное из памяти в ST (0) Упакованное десятичное из памяти в ST (0) ST (/') в ST (0) FST = запомнить ST (0) как целое/вещественное в памяти ST (0) в ST (/) FSTP = запомнить и извлечь из стека ST (0) как целое/веще-1 ственное в памяти ! ST (0) как длинное целое в памяти ST (0) как временное вещественное в памяти ST(0) как упакованное! десятичное в памяти ST(0) bST(/) ! FXCH = обменять ST (/') ' nST(0) l Необязательное 8- или 16-битное смещение Диапазон чм синхрони 2SJS!-32-6mtm •емко* цвлов ела тактов 1 в-бити! MF 00 01 10 11 ESCAPE MF 1 ESCAPE 1 1 1 ESCAPE 0 1 1 ESCAPE 1 1 1 ESCAPE 0 0 1 ESCAPE MF 1 ESCAPE 1 0 1 ESCAPE MF 1 ESCAPE 1 1 1 ESCAPE 0 1 1 ESCAPE 1 1 1 ESCAPE 1 0 1 MOD 0 0 0 R/M j _ DISP MOO 1 0 1 R/M j ~ '_ DISP" MOO 1 0 1 R/M j DISP ' DISP* MOO 1 0 0 R/M 1 1 0 0 0 ST(.) MOO 0 1 0 R/M 110 10 ST(i) MOO 0 1 1 R/M DISP DISP* MOO 1 1 1 R7M MOO 1 1 1 R/M DISP DISP 110 11 ST(i) ESCAPE 0 0 110 0 1 ST(i) Зв-5в S2-60 40-60 46-54 60-66 53-65 290-310 17-22 64-90 62-92 96-104 ft)-90 15-22 66-92 64-94 96-106 62-92 94-105 52-56 520-540 17-24 10-15 Сравнение FCOM = сравнить Целое/вещественное из памяти cST(0) ESCAPE MF 0 MOO 0 1 0 R/M 1 DISP 60-70 76-91 65-75 72-66 ST(/) cST(0) ESCAPE 0 0 0 110 1 0 3T(i) 1 40-50 FCOMP = сравнить и извлечь из стека Целое/вещественное из памяти с ST (0) ST (/) с ST (0) ESCAPE MF 0 MOO 0 1 1 R/M 63-73 60-93 67-77 74-86 ESCAPE 0 0 0 110 1 1 st(o 45-52 FCOMPP = сравнить ST(1) с STtO) и дважды ESCAPE 1 1 0 110 1 1 0 0 1 45-55 извлечь из стека FTST = проверить ST (0) ESCAPE 0 0 1 1110 0 1 0 0 36-46 FXAM = проанализиро- i i ВЭТЬ ST (0) I ESCAPE 001|11 100101 269
Система команд процессора 80287 (продолжение) Н«о6язат*л*П Диапазон числа тактов Загрузка констант. FLDZ = загрузить +0.0 ST(0) FLD1 = загрузить +1.0 ST<0) FLDPI" загрузить* в ST(0) FLDL2T * загрузить log210eST(0) FLDL2E * загрузить k>g2eBST(0) FLDLG2 * загрузить tog102BST(0) FLDLN2 * загрузить loge2eST(0) Арифметические FADD = сложение Целого/вещественного из памяти и ST (0) ST(/) и ST(0) FSUB = вычитание Целого/вещественного из памяти и ST (0) ST(/) hST(0) FMUL = умножение Целого/вещественного из памяти и ST (0) ST(/) иЭТ(0) FDIV=деление Целого/вещественного из памяти и ST (0) ST (/) и ST (0) FSQRT = извлечение корня квадратного из ST(0) FSCALE = масштабирование ST(0) на ST(1) FPREM - частичный остаток ST(0) + ST(1) FRNDINT = округление ST (0) до целого —Щ*сг- iSfiSi™ 1ввщвсг J™94 р10* риио§ ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE MF О ESCAPE d Р О ESCAPE MF О ESCAPE d Р О ESCAPE MF О ESCAPE d P О ESCAPE MF 0 ESCAPE d P 0 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 ESCAPE 0 0 1 10 1110 10 10 0 0 10 10 11 10 10 0 1 10 10 10 10 110 0 10 110 1 MOO 0 0 0 R/M 1 1 0 0 0 ST(i) MOO 1 0 R R/M MOO 0 0 1 R/M MOO 1110 0 11-17 15-21 16-22 16-22 15-21 16-24 17-23 90-120 106-143 95-125 102-137 70-100 (примечание 1) 90-120 106-143 95-125 102-137 70-100 (примечание 1) 110-125 130-144 112-166 124-136 ее us (примечание 1) 215-225 2Э0-243 220-230 224-236 193-203 (примечание 1) 160-166 32-36 15-190 Примечание: если Р = 1, то прибавить 5 тактов синхронизации 270
Система команд процессора 80287 (продолжение) FXTPACT = выделение компонентов ST (0) FABS = абсолютное значение ST (0) FCHS = изменение знака [ Необязательное Диапазон 8- или 16-битное числа тактов смещение синхронизации escape 0 0 escape 0 0 1 escape 0 0 1 escape 0 0 1 escape 0 0 1 ST (0) Трансцендентные FPTAN = частичный тан-f- гене FPATAN = частичный арктангенс F2XM1 = 2ST(0) -1 FYL2X = ST(1) x k>g2 ST(0) | ГзЖГ5Т,,,ХЬ92С^111 Управление процессором escape 0 0 1 escape 0 0 1 FINIT = инициализиро- I escape о i i вать сопроцессор 1 FSETPM = ввести за щи-1 escape о i i щенный режим I FSTSW AX = запомнить слово состояния FLDCW = загрузить слово управления FSTCW = запомнить ело-г во управления |_ FSTSW = запомнить ело- Г во состояния L escape 1 1 1 escape 0 0 1 escape 0 0 1 escape 1 0 1 FCLEX = сбросить осо- I escape о 1 1 бые случаи I I FSTENV = запомнить i среду I escape о о j FLDENV = загрузить : среду FSAVE = запомнить полное состояние escape 0 0 1 "| escape FRSTOR = восстановить! полное состояние [escape 1 о 1 FINCSTP = инкремент i указателя стека I escape о о i IDECSTP= декремент {указателя стека | escape 110 10 0 1 0 0 0 0 1 1 0 0 0 0 0 1 Э 0 1 о 110 0 11 1 1 0 0 0 0 1 1 0 0 0 1 1 0 0 0 1 1 10 0 10 0 1 0 0 0 0 0 mod 1 0 1 moo 1 1 1 r/m moo 1 1 1 r/m 1 1 1 0 0 0 1 0 moo 1 1 о mod 1 о о r/m mod 1 1 о r/m moo 1 о о r/m 11 110 111 11 110 110 disp disp disp 27-55 10-17 10-17 30-540 250-800 310-630 900-1100 700-1000 2-6 2-6 10-16 7-14 12-16 12-18 2-6 40-50 35-45 205-215 205-215 6-12 6-12 271
Система команд процессора 80287 (продолжение) Диапазон числа тактов синхронизации ST(/) ' . , 9-16 FNOP = пустая операция | escape 1 0 1 1 1 0 0 0 st(i) | I escape 0 0 1 1 .1 0 1 0 0 0 0 |
Б.1. ПРИМЕЧАНИЯ 1. Если mod = 00, то DISP = 0 (за исключением случая: если mod = 0 и г/т = 110, то ЕА « « disp-high: disp-low a disp-high и disp-low отсутствуют). Если mod = 01, то DISP = disp-low с расширением со знаком до 16 бит, a disp-high отсутствует. Если mod в Ю, то DISP = disp-high: disp-low. Если mod = 11, то r/m считается полем ST (i). 2. Если r/m - 000, то ЕА = (ВХ) + (SI) + DISP, если г/т = 001, то ЕА = (ВХ) + (DI) + DISP, если r/m = 010, то ЕА = (BP) + (SI) + DISP, если г/т = 011, то ЕА = (BP) + (DI) + DISP, если r/m =100, то ЕА = (SI) + DISP, если r/m = 101, то ЕА = (DI) +DISP, если r/m = ПО, то ЕА = (BP) + DISP, если r/m = 111, то ЕА = (ВХ) + DISP, 3. MF = формат памяти: 00 — 32-битное вещественное, 01 — 32-битное целое, Ю - 64-битное вещественное, 11 - 16-битное целое. 4. ST (0)« текущая вершина стека, ST (0 = i-й регистр под вершиной стека. 5. d = приемник 0 - приемник ST (0) 1-приемник ST (i). 6. Р = извлечение из стека: 0 — нет извлечения, 1 — извлечение из стека ST (0). 7. R — обратная операция; когда d = l, изменяется смысл R: 0 — приемник (операция) источник, 1 — источник (операция) приемник. 8. Для команды FSQRT: -0 < ST (0) < + 00 для команды FSCALE: -215 < ST (1) < +215 и ST (1) - целое число; для команды F2XM1: 0 < ST (0) < 2"\ для команды FYL2X: 0 < ST (0) < «>, -<*> < ST (1) < +«»; для команды FYL2XP1: 0 < | ST (0) | < (2 - ^2)/2, -00 < ST (1) < +«> для команды FPTAN: 0 < ST (0) ^ л/4, для команды FPATAN: 0 < ST (1) < ST (0) < +«. 9. Двоичный набор ESCAPE равен 11011. 10 Заказ 6126 273
ПРИЛОЖЕНИЕ В ПРОСТРАНСТВО КОДОВ ОПЕРАЦИЙ ПРОЦЕССОРА 80286 В большинстве команд процессора 80286 код операции полностью содержится в первом байте команды. Однако в некоторых командах код операции занимает определенные биты следующих байт. Часть кода операции, находящаяся в первом байте команды, называется первичным кодом операции, а остальная часть (если она имеется) - вторичным кодом операции. Ниже показано размещение команд процессора 80286 в матрице, называемой пространством кодов операций. Элементы матрицы соответствуют кодам операций команд. Каждый элемент содержит мнемонику команды, имеющую этот код операции, а также состояния всех полей, которые отличают код операции от других кодов операций, имеющих одну и ту же мнемонику команды. Например, первичные коды операций АС и AD соответствуют мнемонике LODS (загрузить цепочку). Элемент пространства первичных кодов операций AD (на пересечении строки А и столбца D) содержит мнемонику LODS w, показывая, что эта команда загружает слово (поле w равно 1). Элемент для АС содержит просто мнемонику LODS, показывая загрузку байта (поле w равно 0). Каждый элемент в матрице определяет не только команду, но и все аргументы, используемые командой. Для задания аргументов применяются следующие обозначения: 1. r/m означает один из аргументов, определяемый полями mod и r/m. 2. reg означает один из аргументов, определяемый полем reg. 3. imm означает один из аргументов, определяемых как непосредственные данные. 4. mem означает непосредственный адрес памяти одного из аргументов. 5. Бели явно указано название регистра, этот регистр является одним из аргументов. 6. При наличии двух аргументов первым указывается аргумент-приемник. Рассмотрим, например, команду с кодом операции 29. Элемент пространства первичных кодов операций, находящийся на пересечении строки 2 и столбца 9, показывает, что это команда SUB (вычитание). Поле w ■ 1 показывает операцию вычитания слов. Далее, операнд-приемник определяется полем r/m (и mod), а операнд-источник опеределяется полем reg. Следовательно, эта команда вычитает содержимое операнда-слова, определяемого полем reg, из содержимого операнда-слова, определяемого полями mod и r/m, и помещает результат в операнд, идентифицируемый полями mod и r/m. Чтобы показать использование вторичного кода операции, рассмотрим команду с первичным кодом операции F7. Пространство первичных кодов операций содержит три звездочки (***) на пересечении строки F и столбца 7, показывая наличие вторичного кода операции. Элемент пространства первичных кодов операций содержит также w, показывая операцию со словами. Элемент пространства вторичных кодов операций для F7 находится по строке, обозначенной F7. Имеется семь команд, у которых первичный код операции равен F7. Предположим, что наша команда в коде операции, находящемся во втором байте, содержит 011. Элемент пространства вторичных кодов операций для строки F7 и столбца 011 содержит NEG r/m. Следовательно, команда изменяет знак слова, определяемого полями mod и r/m. 274
... Смысл смотри в пространстве вторичных кодов операций Неиспользуемый код операции Рис. В.1. Пространство первичных кодов операций
Байт 2, биты 5 :3 Первичный ^ операции ООО 001 010 011 100 101 110 111 80-81 Abb r/m. Imm 1 Ф 1 r/m. Imm ADC r/m. Imm 1 SBB I AND 1 r/m. imm xGA r/m. imm ЬМ> i r/m. imm i ООО 001 010 011 100 101 110 111 вз абь r/m. imm abb r/m. Imm r/m. imm sob r/m. imm t№ i r/m. imm i ООО 001 010 011 100 101 110 111 POP r/m ООО 001 010 011 100 101 110 111 №81 ROL r/m i r6A i r/m RCL r/m RCR r/m 1 SHL/SAL 1 r/m SHR r/m &AR i r/m i 000 001 010 011 100 101 110 111 F6-F7 r/m. Imm r/m i r/m I Mut I r/m IMUL r/m DIV r/m IDIV r/m 000 001 010 011 100 101 110 111 № r/m I bed I f/m ООО I bSL 010 011 100 101 110 111 FF №w r/m I- r'm CALL , LP1UL UAll I, №a JttK-j кип. 8 Неиспользуемый код операции Первичный код . операции f 000 001 Байт 2, биты 2 :0 -•> | SLDT | STR | LLDT | LTR | VERR |VERVv] в Неиспользуемый код операции Рис. В.2. Пространство вторичных кодов операций 276
ПРИЛОЖЕНИЕ Г ПРОСТРАНСТВО КОДОВ ОПЕРАЦИЙ ПРОЦЕССОРА 80287 Коды операций процессора 80287 занимают в пространстве кодов операций процессора 80286 вторую половину строки D. Следовательно, каждая команда процессора 80286 с кодами операций от D8 до DF в первом байте содержит и код операции процессора 80287. Код операции процессора 80287 занимает три младших бита в первом байте и некоторые биты второго байта. Старшие два бита 7 : 6 второго байта показывают, производит команда обращение к памяти или нет (если хотя бы один из них содержит 0, команда обращается к памяти). На приведенных ниже диаграммах показаны пространства кодов операций процессора 80287 для команд с обращением к памяти и без него. Рассмотрим, например, команду, первый байт которой содержит код D9 (1101 1001), а биты 7 : 6 второго байта содержат код И. Это команда без обращения к памяти, поэтому следует пользоваться рис. Г.2. Код D9 показывает на строку с возможными командами. Конкретную команду определяют биты 5:3 во втором байте. Пусть наша команда содержит в этих битах 001. Тогда получается команда FXCH ST (i). Предположим далее, что команда содержит код 111 в битах 5 : 3. Код 111 показывает на другую строку возможных команд, определяемых битами 2:0 второго байта. Положим, что наша команда содержит 000 в битах 2:0 второго байта (т. е. второй байт имеет вид 11 111 000). По диаграмме находим, что получается команда FPREM.
Конец строки D пространстве первичных кодов операций of" reel I Обозначения: (16 1): 16-битное целое (32 I): 32-битное целое (64 I): 64-битное целое (32 R): 32-битное вещест венное (64 R): 64-битное вещественное (80 В):80-битное деся- ■тичное = Неиспользуемый код опе- рации *8 u i 278
Обозначения: i Неиспользуемый код операции * Избыточный код операции Байт 2, биты 5 :3 < щ 001 010 011 100 101 110 FADO fMJL FCOM FCOMP FSUB F8UBR FWV FMVR 6T,ST(0 8Т,8Т(0 8T(D 8T(i) 8T,8T<i) 8T,8T<i) ST,ST(I) 8T.ST0) 279
Рис. Г.2. Команды без обращения к памяти (байт 2, биты 7:6 = 11)
ПРИЛОЖЕНИЕ Д ПЕРЕДАЧА СООБЩЕНИЙ МЕЖДУ ЗАДАЧАМИ Задачи могут взаимодействовать друг с другом одним из двух способов: через разделенную память или путем передачи сообщений. В первом варианте задачи передают информацию, обращаясь к разделяемому между ними сегменту. Способы разделения сегментов рассмотрены в гл. 5. Одна из трудностей в этом варианте заключается в том, что разделяющие сегмент задачи должны как-то синхронизировать свои действия, чтобы задача не обращалась к сегменту до тех пор, пока он находится в неустановившемся состоянии. Примеры этой трудности Обсуждались в гл. 3 при рассмотрении префикса LOCK и семафоров. Хотя там мы говорили о нескольких процессорах, аналогичные рассуждения применимы и к нескольким задачам. Еще одна трудность с разделенной памятью состоит в том, что при использовании альтернативных имен дескрипторов (наиболее сложный способ разделения) затрудняется управление виртуальной памятью. Альтернативное именование и связанные с ним сложности обсуждались в гл. 5. Передача сообщений оказывается более медленным и менее гибким способом, чем разделенная память, но при этом не требуется альтернативное именование и обеспечивается автоматическая синхронизация. Передача сообщений очень похожа на пересылку письма по почте. Мы можем передать сегмент (эквивалент письма в процессоре 80286) из одной задачи в другую, копируя дескриптор сегмента из LDT передающей задачи в LDT принимающей задачи. Если желательно избежать альтернативного именования, после копирования можно уничтожить старый дескриптор. Таким образом, сегмент или сообщение удаляется из адресного пространства передатчика и помещается в адресное пространство приемника. Так как копируется только дескриптор, а не сам сегмент, время, необходимое для передачи сообщения, не зависит от размера сообщения. По-видимому, простейшая реализация передачи сообщений состоит из двух процедур send (передача) и receive (прием). Процедура send вызывается передающей задачей для пересылки сообщения указанной принимающей задаче. Процедура receive вызывается принимающей задачей для получения сообщения от указанного передатчика. Параметрами этих процедур являются: send: message - селектор (в LDT передатчика), идентифицирующий передаваемый сегмент-сообщение, receiver - селектор (в GDT), идентифицирующий сегмент состояния принимающей задачи, receive: message - селектор (в LDT приемника), который будет идентифицировать принимаемый сегмент-сообщение, sender — селектор (в GDT), идентифицирующий сегмент состояния передающей задачи. Отметим, что дескриптор, на который первоначально ссылался селектор сообщения receive, будет заменен дескриптором, который передается процедуре send. Процедуры send и receive работают по-разному в зависимости от того, выполняет ли задача-передатчик процедуру send до того, как приемник выполняет соответствующую процедуру receive, или наоборот. Если процедура send выполняется раньше receive, то задача-передатчик приостанавливается (т. е. будет ожидать) до тех пор, пока задача-приемник не выполнит соответствующую процедуру receive. Если же процедура receive выполняется раньше send, то задача-приемник приостанавливается до тех пор, пока передатчик не выпол- 281
кит соответствующую процедуру send. Такой вид синхронизации задач иногда называется рандеву, так как задача, которая "приходит* первой, ожидает вторую задачу, а затем они "прогуливаются вместе". Для реализации рандеву мы добавим в каждый сегмент состояния задачи два программно-определяемых поля: поле event размером в слово (как и размер селектора) и 8-байтное поле msg (как и размер дескриптора). Кроме того, мы дополним список готовых к выполнению задач (см. гл. 5) переменной last, содержащей селектор последнего сегмента состояния задачи в списке готовности, как показано на рис. Д.1. Теперь, если задача А хочет ожидать задачи В (осуществляя передачу в В или прием от В), она (т.е. задача А) помещает в свое поле event селектор для задачи В. После этого задача А исключает себя из списка готовности и процессор 80286 переходит к следующей по порядку задаче. Задача В может проверить, ожидает ли ее задача А (осуществляя передачу в А или прием от А), анализируя поле event задачи А. Регистр задачи Выполняющаяся Последняя laat задача задача link link link •vent •vent •vent msQ fH9Q Рис. Д.1. Список готовности сегментов состояний задач, модифицированных для передачи сообщений Таким образом, процедура send прежде всего проверяет, не ожидает ли ее приемник (receiver). Если это так, процедура send копирует дескриптор сообщения в поле msg сегмента состояния задачи-приемника и помещает приемник в конец списка готовности. Бели же нет, то send копирует дескриптор в свое поле msg и ожидает приемника, исключая себя из списка готовности. Процедура receive прежде всего проверяет, не ожидает ли ее передатчик (sender). Бели это так, то receive копирует поле msg передатчика в LDT и помещает передатчик в конец списка готовности. Бели же нет, то процедура receive ожидает доступности передатчика, исключая себя из списка готовности. Когда процедура receive возобновляется, она копирует в LDT поле msg (запомненное процедурой send). Ниже показано кодирование процедур send и receive на языке типа Паскаль. Обозначение х:у подразумевает ячейку, адресуемую селектором х и смещением у. Обозначение LDT [s] относится к дескриптору в текущей LDT, соответствующему селектору s. Вспомогательная процедура dequeue исключает текущую задачу из списка готовности и осуществляет переключение на следующую задачу в списке. Вспомогательная процедура enqueue добавляет задачу, определяемую аргументом процедуры, в конец списка готовности, procedure send(message, receiver) begin CLIj Ссбросить флажок прерываний} 282
if receiver:event « task_register then begin receiver:event := 0; receiver:msg :« LDTCmessageD; LDTСmessage3 := 0; enqueue(recei ver) end else begin task_register:event :■ receiver; task_register:msg : = LDTCmessage}; LDTСmessaged := 0; dequeue end; STI (установить флажок прерываний} end procedure receive (message, sender) begin CLI; Ссбросить флажок прерываний} if sender:event = task_register then begin sender:event := 0; LDTCmessage3 :« sender:msg; sender:msg := 0; enqueue(sender) end el se begin task_register:event := sender; dequeue; LDTСmessage! := task_register:msg; task_register:msg := 0 end; STI (установить флажок прерываний} end Довольно легко преобразовать эти процедуры на язык ассемблера процессора 80286. Небольшая трудность возникает в связи с тем, что дескрипторы таблицы LDT и дескрипторы сегментов состояния задач нельзя использовать в явных операциях считывания-и^записи. Напомним, что эта трудность преодолевается, если всегда хранить соответствующий дескриптор сегмента данных с альтернативным именем после каждого из этих специальных дескрипторов. Подробнее об этом приеме написано в примере с разделением времени в гл. 5. Ниже приведены ассемблерные варианты процедур receive, enqueue и dequeue. Процедура send почти идентична процедуре receive, поэтому она не приводится. receive PROC FAR : receive (message, sender) MOV BP.SP ; message в BP+6. sender в BP+4 283
PUSH DS NOV АХ.4СВРЭ MOV BX.AX ADD AX,8 MOV DS,AX ASSUME DStTSS STR AX CLI CMP AX,event JNE not.yet MOV event,0 CALL enqueue i сохранить DS t ifendtr e AX i гохмнить sender- e BX i вычислить альтернативный сегмент i данных для TSS передатчика I теперь момно считывать/записывать з TSS передатчика I селектор для текущего TSS е АХ з прерывания запрещены I ожидает ли передатчик текущую задачу? i перейти, если передатчик не ожидает I передатчик больше ожидать не должен ; добавить TSS передатчика в конец ; списка готовности move.megs SLDT АХ ADD АХ,8 MOV ES,AX ASSUME ESiLDT MOV АХ.6СВРЭ AX ,0FFF8H DI .AX SI,OFFSET CX.4 AND MOV MOV MOV CLD REP MOV STI POP RET MOVSW BYTE PTR DS 4 i селектор для текущей LDT в AX ; вычислить альтернативный сегмент ; данных для LDT t теперь можно записывать в LDT з селектор сообщения в АХ t сбросить KPL и TI в нуль 5 смещение дескриптора сообщения в АХ msg з смешение поля msg в TSS I длина дескриптора 4 слова ; пересылка цепочки вперед I передать дескриптор ив TSS в LDT 6CSI3,0 i уничтожить дескриптор в TSS з разрешить прерывания : восстановить DS з возврат MOV АХ.З : вычислить альтернативный сегмент ; данных для LDT DS.AX з теперь можно считывать/записывать ; текущий TSS ASSUME DSrTSS MOV event,ВX * ожидание передатчика CALL dequeue ; ожидать«когда передатчик : пошлет дескриптор JMP move,,msg ; возобновить отсюда после получения дескриг-ора receive ENDP 284
enqueue PROC NEAR MOV MOV link*2,AX ES,SEG last ASSUME ESeSEG last MOV ADD MOV MOV AX,1ast AX,8 last,BX ES,AX I помещает TSS передатчика I e конец списка готовности I DS « TSS<г/и) передатчика, с АХ « текущий TSS, ; ВХ * TSS передатчика I link передатчика адресует з текущий TSS ; теперь можно считывать/ з записывать last 3 селектор последнего TSS 3 в списке готовности •; вычислить альтернативный сегмент ; данных для последнего TSS ; теперь Передатчик последний в списке ; теперь можно записывать в старый. ; последний TSS nqueue dequeue ASSUME ES;TSS MOV ESilink+2,BX RET ENDP PROC MOV NEAR ; ES,SEG last link старого последнего TSS адресует новый последний удаляет текущую задачу из списка готовности и производит переключение DS * текущий TSS(r/w) ; теперь можно считывать/ ; записывать last ASSUME ESsSEG last MOV AX,1ast ; селектор последнего TSS в списке ; готовности ADD АХ,8 i вычислить альтернативный:Сегмент ; данных для последнего TSS MOV ES,AX ; теперь можно записывать 3 в последний TSS ASSUME ES:TSS MOV AX,DSslink+2 MOV ESslink+2,AX JMP ; селектор следующего TSS в списке ; link последнего TSS адресует ; новый первый TSS DWORD. PTR DS:link ; переключается на новую 9 первую задачу RET ; возобновить отсюда,когда происходит ; обратное переключение dequeue ENDP При частоте синхронизации 8 МГц пара процедур выполняется примерно 0,11 мс, что соответствует передаче около 9000 сообщений в секунду.
ПРИЛОЖЕНИЕ Б СЛОВАРЬ ТЕРМИНОВ Access byte — байт доступа: поле в дескрипторе, содержащее информацию о защите и идентифицирующее тип дескриптора. Accumulator — аккумулятор: альтернативное название регистра АХ. Address decoding— дешифрирование (декодирование) адреса: преобразование адреса, находящегося на шине, в один сигнал, который либо выбирает, либо запрещает конкретное устройство. Address space — адресное пространство: все байты памяти, доступные программе. Address translation — преобразование адреса: преобразование селектора и смещения в физический адрес. Addressing mode — режим адресации: способ определения операнда в команде. Affine mode — аффинный режим: режим работы процессора 80287, в котором бесконечности могут использоваться во всех операциях, где это имеет смысл. Положительная и отрицательная бесконечности считаются различными. Aliases — альтернативные имена: два или более дескрипторов, обращающихся к одному и тому же сегменту. ALS (Advanced Low-power Schottky) — усовершенствованные маломощные микросхемы с диодами Шотки: технология производства электронных компонентов. Большинство микросхем, применяемые для подключения к процессору 80286 памяти и устройств ввода-вывода, выполнены по этой технологии. Architecture — архитектура: интерфейс между аппаратными средствами и программой. Это минимальные сведения о компьютере, которыми должен обладать программист, работающий на языке Ассемблера. Argument reduction — приведение аргумента: приведение чисел в диапазоны, разрешенные для трансцендентных команд процессора 80287. AS (Advanced Schottky) - усовершенствованная технология с диодами Шотки: аналогична ALS-технологии, но обеспечивает повышенное быстродействие за счет большего потребления мощности. ASCn — Американский стандартный код для обмена информацией: код, определяющий двоичное кодирование букв А — Z, а — Z, цифр 0-9, знаков пунктуации и специальных символов. Assembler — ассемблер: программа, осуществляющая трансляцию с языка ассемблера на машинный язык. Assembler directive — директива ассемблера: конструкция языка ассемблера, которая не генерирует команд, а сообщает ассемблеру служебную информацию. Assembly language — язык ассемблера (ассемблирования): символическая система обозначений для записи машинных команд. Язык ассемблера более удобен для представления команд, чем запись цепочек из единиц и нулей. Backwords — "обратная" память: образное описание хранения слов в памяти: младший байт по меньшему адресу. Bank — банк: совокупность аппаратных средств памяти. Память компьютера обычно состоит из нескольких банков, чтобы повысить производительность за счет одновременного доступа к различным банкам. Base address — базовый адрес: физический адрес начала сегмента. 286
Bias — смещение (порядка): константа, прибавляемая к истинному порядку числа с плавающей точкой. Применяется для того, чтобы избежать отрицательных чисел в поле порядка. Binary-coded decimal (BCD) - двоично-кодированное десятичное (число): представление чисел, в котором каждая десятичная цифра кодируется четырьмя битами. Binary point - двоичная точка: понятие двоичной системы счисления, аналогичное десятичной точке. Например, в двоичном числе 101.11 двоичная точка отделяет целую часть 101- 5 от дробной части .11 = 1/2 +1/4 • 3/4. Bit — бит (двоичная цифра): бит принимает значения 0 или 1. Buffer - буфер: компонент для усиления сигнала. Буфер неизбежно вносит задержку в тракт распространения сигнала. Bus - шина: набор линий в компьютере; к шине подключаются память и периферийные устройства. Bus command - команда шины: сигнал, инициирующий выполнение на шине конкретной операции. Обычно имеются пять команд шины: считывание из памяти, запись в память, ввод, вывод и подтверждение прерывания. Bus controller - контроллер шины: микросхема, которая формирует команды шины и другие сигналы, декодируя сигналы состояния процессора 80286. Bus cycle - цикл шины: одна передача информации по шине. Bus pipelining — конвейеризация шины: специальный прием, в результате которого устройству для производства передачи по шине отводится больше времени, чем один цикл шины. Конвейеризация не замедляет действия на шине. Byte - байт: единица памяти, состоящая из восьми бит. Central processing unit (CPU) — центральный процессор: компонент компьютера, выполняющий команды программы. Микросхема 80286 - это центральный процессор. Channel-- канал: схемы, осуществляющие обращение в память в режиме прямого доступа к памяти. Chip - кристалл, микросхема, чип. Chip select — выбор(ка) кристалла: сигнал, выбирающий одно устройство на шине. Сигналы выбора кристалла формирует дешифрирования адреса. CMOS (Complementary Metal-Oxide-Semiconductor) - комплементарная МОП-технология (КМОП): технология производства электронных компонентов. Отличается малым потреблением мощности. По этой технологии выполнены микросхемы динамических ЗУПВ. Comparator - компаратор: компонент, сравнивающий два двоичных числа. Compiler — компилятор: программа, осуществляющая трансляцию с языка высокого уровня на машинный язык. Omfcoming — подчинение: свойство сегмента, показывающее, что каждая процедура в этом сегменте при ее вызове перемещается в кольцо вызывающей программы. Continued fraction - цепная дробь: арифметическое выражение вида а + Ь/(р + а7(е +//fe + + ...))). Current privilege level (CPL) - текущий уровень привилегий: уровень привилегий выполняющейся сейчас программы. Хранится в двух младших битах регистра CS. Debugger — отладчик: средство наблюдения за выполнением программы. Отладчики применяются для локализации в программе ошибок. Denormal — денормализованное число: ненулевое число с плавающей точкой, содержащее в поле порядка 00... 0. Денормализованные числа генерируются сопроцессором 80287, когда особый случай численного антипереполнения замаскирован. 287
Descriptor — дескриптор: 8-байтная величина, определяющая независимо защищаемый объект. Дескриптор объекта, кроме информации о защите, определяет базовый адрес объекта или адрес его точки входа. Descriptor cache - кэш-память дескрипторов (см. shadow register). Descriptor privilege level (DPL) - уровень привилегий дескриптора: поле в дескрипторе, показывающее, как он защищен. Бели дескриптор определяет выполняемый сегмент, то уровень привилегий дескриптора показывает, насколько привилегированы процедуры в этом сегменте. Direct addressing - прямая адресация: определение ячейки памяти адресом, встроенным в команду. Direct memory access (DMA) - прямой доступ к памяти (ПДП): способ, позволяющий устройству ввода-вывода считывать или записывать прямо в память, минуя процессор 80286. Displacement — смещение (в команде): 16-битное значение, определяемое в команде и используемое для вычисления эффективного адреса. Display — индикатор: список указателей стековых кадров процедур, в которые вложена текущая выполняющаяся процедура. Индикатор обеспечивает выполняющейся процедуре легкий доступ к переменным в тех процедурах, в которые она вложена. Double precision — двойная точность: формат с плавающей точкой, в котором каждое число занимает 8 байт. DRAM controUer - контроллер ДЗУПВ: микросхема, обеспечивающая интерфейс с динамическим ЗУПВ. Dynamic random access memory (DRAM) — динамическое запоминающее устройство с произвольной выборкой (ДЗУПВ): ЗУПВ, требующее периодической регенерации, чтобы не потерять данные. Elementary functions — элементарные функции: тригонометрические, показательные, логарифмические, гиперболические, обратные тригонометрические и обратные гиперболические функции. Error value — значение ошибки (см. exception value). Escape instruction — команда переключения (на сопроцессор): команда, имеющая код операции ESC. Такие команды выполняет сопроцессор 80287, а не процессор 80286. Exception - особый случай: условие, возникающее, когда команда процессоров 80286 или 80287 нарушает правила обычной операции. В процессоре 80286 особый случай вызывает прерывание. В процессоре 80287 особый случай может вызвать прерывание, но может быть замаскирован, и тогда формируется специальное значение. Exception value — значение особого случая: специальное значение, которое образует команда процессора 80287, если она вызывает замаскированный особый случай. Expand down - расширение вниз: свойство сегмента, заставляющее процессор 80286 контролировать то, что во всех обращениях к этому сегменту смещения больше предела сегмента. Применяется для сегментов стека. Expand up — расширение вверх: свойство сегмента, заставляющее процессор 80286 контролировать то, что во всех обращениях к этому сегменту смещения не больше предела сегмента. Обычно этим свойством обладают все сегменты, кроме сегментов стека. Explicit cache — явная кэш-память (см. shadow register). Exponent - порядок: та часть числа с плавающей точкой, которая определяет масштабный коэффициент. Например, в десятичном числе с плавающей точкой 3,7 х 105 порядок равен 5. 288
Extended precision — расширенная точность: формат с плавающей точкой, в котором каждое число занимает 10 байт. Расширенную точность следует применять только для промежуточных результатов. Field — поле: один или несколько смежных бит, представляющих часть некоторого большего элемента данных. Flag — флажок, флаг: поле в регистре флажков. Флажок показывает состояние ранее выполненной команды или управляет работой процессора. Floating point — плавающая точка: способ приближенного представления вещественных чисел в компьютерах. Gate — шлюз: существуют шлюзы вызова, специального прерывания, прерывания и задачи. Первые три вида шлюзов — это дескрипторы, определяющие точки входа процедуры. Шлюз задачи — это дескриптор, определяющий задачу. Gather write - собирающая запись: запись блока выходных данных посредством сбора содержимого несмежных ячеек памяти. General register - регистр общего назначения: один из 16-битных регистров АХ, ВХ, СХ или ИХ. Global descriptor table (GDI) - глобальная дескрипторная таблица: таблица в памяти, содержащая дескрипторы сегментов, которые разделяются всеми задачами. Gradual underflow — постепенное (плавное) антипереполнение: реакция на численное антипереполнение путем введения ненормализованных чисел. Hexadecimal - шестнадцатеричный: с основанием 16. Шестнадцатеричные цифры: 0, 1, 2, 3, 4,5, 6,7, 8, 9, А (=10), В (-11), С (=12), D (=13), Е (=14), F (=15). High level language — язык высокого уровня: язык программирования, более "удаленный* от машины, чём язык ассемблера. Примерами служат языки Паскаль, Бейсик,Си и Фортран. Immediate operand — непосредственный операнд: константа, содержащаяся в команде и используемая как операнд. Implicit bit - неявный (скрытый) бит: старший бит нормализованного числа с плавающей точкой, который всегда содержит 1 и поэтому опускается. Indefinite — неопределенность: нечисло, которое процессор 80287 формирует на замаскированный особый случай недействительной операции, если ни один из операндов не является нечислом. Index register - индексный регистр: один из 16-битных регистров SI или DI. Обычно индексный регистр содержит смещение в текущем сегменте данных или дополнительном сегменте. Indirect addressing - косвенная адресация: обращение к ячейке памяти, когда нужный адрес вначале берется из регистра или другой ячейки памяти. Infinity — бесконечность: число с плавающей точкой, в котором поле порядка содержит 11... 1, а поле мантиссы 00... 0. В процессоре 80287 бесконечность формируется как реакция на замаскированные особые случаи переполнения или деления на нуль. Instruction - команда, инструкция: одно действие в программе. Instruction pointer (IP) - указатель команды: регистр, содержащий смещение выполняющейся команды. Селектор сегмента, содержащего эту команду, находится в регистре CS. Integrated circuit — интегральная микросхема: электронная схема, изготовленная на одном кристалле кремния. 289
/ Interleaving — расслоение: способ повышения производительности памяти. Последовательные ячейки памяти находятся в разных банках с циклическим повторением. Поэтому при обращениях к последовательным ячейкам обеспечивается одновременная работа нескольких блоков. Interrupt — прерывание: принудительный вызов процедуры, явно не фигурирующий в программе, а осуществляемый в результате особого случая, сигналом от внешнего устройства или специальной командой прерывания. Interrupt controller — контроллер прерываний: микросхема, которая упорядочивает несколько запросов прерываний в соответствии с их приоритетами. Interrupt descriptor table (IDT) - дескрипторная таблица прерываний: таблица в памяти, индексируемая номером прерывания и содержащая шлюзы соответствующих процедур прерываний. Interrupt distributor — распределитель прерываний: процедура обработки прерывания в составе операционной системы, которая в зависимости от прерванной задачи переходит к различным процедурам прерываний, разработанным пользователем. Interrupt handler - обработчик прерывания: процедура или задача, вызываемые в ответ на прерывание. Interrupt procedure — процедура прерывания: процедура, вызываемая в ответ на прерывание. Interrupt task — задача прерывания: задача, активизируемая в ответ на прерывание. Interval timer — интервальный таймер: микросхема, прерывающая процессор 80286 по истечении временного интервала. Программа может задать сам интервал и определить г ерио- дические или однократные прерывания от таймера. Latch — защелка: микросхема, запоминающая по команде значения входных сигналов. Local address space - локальное адресное пространство: совокупность сегментов, доступных через таблицу LDT задачи. Local descriptor table (LDT) — локальная дескрипторная таблица: таблица в памяти, содержащая дескрипторы сегментов, которые являются "частной собственностью" задачи. Lock — блокировка: сигнал от одного из процессоров мультипроцессорной системы, запрещающий другим процессорам доступ к памяти. Процессор монопольно использует память до тех пор, пока не снимет сигнал блокировки. Long integer — длинное целое число: в процессоре 80287 64-битное целое число. Mashine status word (MSW) - слово состояния машины: регистр процессора 80286, содержащий бит управления режимом работы (реальный или виртуальный) и биты, управляющие выполнением команд WAIT и ESC. Masked exception - замаскированный особый случай: в процессоре 80287 особый случай, который вместо прерывания формирует специальное значение. В зависимости от содержимого регистра управления особые случаи могут быть замаскированными или нет. Masking — маскирование: средство проверки в слове только определенных бит. Обычно реализуется путем объединения по И слова с маской, содержащей единицы в нужных битах. Memory - память: носитель для хранения программ и данных. Memory management - управление памятью: средства процессора 80286 для отображения адресного пространства задачи на имеющуюся память. Memory-mapped input/output — ввод-вывод, отображенный на память: способ ввода-вывода, в котором периферийное устройство выглядит как ячейка памяти. Запись в память с указанием этой ячейки превращается в вывод, а считывание из памяти - во ввод. Message-passing — передача сообщений: способ взаимодействия между задачами, в котором из одной задачи в другую передаются данные. 290
Microcomputer — микрокомпьютер: компьютер на базе микропроцессора. Microprocessor — микропроцессор: центральный процессор, реализованный на одном кристалле. Mnemonic - мнемоника: символическое имя, например для кода операции. Мнемонику запомнить проще, чем двоичный код операции. Multiple-precision arithmetic — арифметика многократной точности: арифметические операции над целыми числами (размер которых больше длины машинного слова). N-channel Metal-Oxide-Seniiconductor (NMOS) - N-канальная МОП-технология; технология производства электронных компонентов. По этой технологии выпускаются процессоры 80286 и 80287. Not present - не присутствующий (отсутствующий): в системе виртуальной памяти сегмент, который находится на диске, а не в основной памяти. Nomnaskable interrupt (NMI) — немаскируемое прерывание: сигнал процессору 80286 от внешнего устройства о катастрофическом событии. Normalised - нормализованный: двоичное число с плавающей точкой нормализовано, если старший бит мантиссы содержит 1. Not-a-Number (NaN)- нечисло: специальное значение, формируемое процессором 80287 в ответ на замаскированный особый случай недействительной операции. Null selector - пустой селектор: селектор, в котором все биты (возможно, за исключением RPL) равны нулю. Offset- смещение: 16-битная величина, определяющая положение байта в сегменте. Opcode - код операции (КОП): часть команды, определяющая выполняемую операцию, в отличие от объектов, над которыми выполняется операция. Operand — операнд: объект, используемый командой как входное или выходное значение. Operand addressing mode- режим адресации (см. addressing mode). Ожег flow- переполнение: 1) особый случай, возникающий, когда число по абсолютному значению слишком велико для представления; 2) переполнение стека - особый случай, возникающий, когда в стек включается слишком много данных. Packed BCD - см. packed decimal. Packed decimal- упакованное десятичное число: представление целых чисел, в котором каждая десятичная цифра занимает 4 бита, а в каждом байте находятся две десятичные цифры. Parameter - параметр: элемент, передаваемый процедуре вызывающей программой. Parity — паритет (четность): указание того, содержит число четное или нечетное число единиц. Флажок паритета PF в процессоре 80286 показывает, содержит ли байт четное число единиц. Peripherial device - периферийное устройство (интерфейсная микросхема): электронное устройство, подключенное к процессору 80286. Периферийные устройства взаимодействуют с процессором при помощи команд ввода и вывода, прерываний и ввода-вывода, отображенного на память. Physical address — физический адрес число, передаваемое в схемы памяти для определения условий ячейки. Pointee — указываемый;элемент в памяти, на который ссылается указатель. Pointer — указатель: 32-битный адрес, используемый программами и состоящий из селектора и смещения. В реальном режиме указатели являются реальными адресами, а в виртуальном режиме — виртуальными. . 291
Pointer register- указательный регистр: один из 16-битных регистров BP или ВХ. Обычно указательный регистр содержит смещение в текущем сегменте стека. Pole — полюс: в рациональной функции значение аргумента, при котором знаменатель равен нулю. Position-independent code (PIC)— позиционно-независимый код: код (программа), выполняемый правильно независимо от своего размещения в памяти. Precharge - предзаряд: интервал успокоения, необходимый между обращениями к микросхемам динамических ЗУПВ. Prefix— префикс: байт, предшествующий коду операции команды и определяющий, что команда должна быть повторена, заблокирована или должна использовать другой сегмент. Privilege level - уровень привилегий: число в диапазоне от 0 до 3, показывающее степень защиты, степень привилегий или степень ответственности. Procedure - процедура: часть программы, выполняющая конкретную функцию, которую можно использовать в нескольких местах программы. Process-процесс (см. task). Processor- процессор (см. central processing unit). Processor clock - синхронизация процессора (см. processor cycle). Processor cycle - цикл процессора: единица, используемая для измерения времени выполнения команды. Programmable read-only memory (PROM) - программируемое постоянное запоминающее устройство (ППЗУ): микросхема памяти, в которой биты, содержащие единицы, нельзя изменить на нули. Обычно для установки бит в единицы применяется специальный прибор (программатор). Projective mode — проективный режим: режим работы процессора 80287, в котором бесконечности можно использовать только в безопасных операциях. Положительная и отрицательная бесконечности считаются одинаковыми. Protected virtual address mode - защищенный режим виртуального адреса: режим работы процессора 80286, в котором реализованы мультизадачность, виртуальная память и совершенные средства защиты. Random access memory (RAM) — запоминающее устройство с произвольной выборкой (ЗУПВ): разновидность памяти, в которой можно считывать и записывать данные, причем время доступа почти не зависит от конкретного адреса. , Rational function— рациональная функция: частное от деления двух полиномов. Ready list - список готовности: список задач, ожидающих использования процессора. Real address— реальный адрес: 32-битный адрес, содержащий селектор и смещение в режиме реального адреса. Real address mode- режим реального адреса: режим работы процессора 80286, в котором он имитирует поведение микропроцессора 8086. Real mode - реальный режим (см. real address mode). Real-time system— система реального времени: вычислительная система, которая должна быстро реагировать на внешние стимулы. Примерами служат системы управления космическими аппаратами, научными приборами и др. Reentrant procedure — реентраитная (реентерабельная) процедура: процедура, которую можно вызывать, когда она уже выполняется от предьцущего вызова. Refresh — регенерация: считывание с последующей записью ячеек динамического ЗУПВ. Ячейки такой памяти необходимо периодически регенерировать, иначе содержащиеся в них данные теряются. 292
Register - регистр: память, находящаяся внутри конкретного устройства. Устройство обращается к своим регистрам быстрее, чем к внешней памяти. Rendezvous - рандеву: способ синхронизации двух задач, в котором первая задача, достигшая точки синхронизации, ожидает вторую. Reguested privilege level (RPL) - запрошенный уровень привилегий: поле в селекторе, показывающее степень ответственности программы. Reset - сброс: сигнал, который вызывает реинициализацию аппаратных средств компьютера. Сброс аппаратных средств является частью инициализации системы. Restartable - перезапускаемый, рестартируемый: команда называется перезапускаемой после конкретного особого случая, если с устранением его причины можно продолжить правильное выполнение программы путем повторного исполнения команды. Ring — кольцо (см. privilege level). Rounding- округление: способ уменьшения ошибки в числе для размещения числа в формате с меньшей точностью. В процессоре 80286 имеется четыре способа округления. Scatter read- рассеивающее Считывание: считывание блока входных данных в несмежные ячейки памяти. Security kernel - ядро безопасности: часть операционной системы, обеспечивающая защиту и безопасность. Segment - сегмент: область памяти, содержащая до 64К байт. Segment register- сегментный регистр: один из регистров CS, DS, SS или ES. Каждый сегментный регистр содержит селектор сегмента. Selector — селектор: 16-битная величина, определяющая сегмент. Селекторы действуют по-разному в реальном и виртуальном режимах. Semaphore - семафор: разделяемая задачами или процессорами переменная, которая применяется для синхронизации. Нулевое значение семафора означает продолжать, а ненулевое ожидать. Shadow register - теневой регистр. Скрытый регистр, ассоциированный с "видимым" регистром. Теневой регистр содержит дескриптор, соответствующий селектору в "видимом" регистре. Shutdown— отключение: устойчивое состояние, из которого процессор 80286 выводится только сигналами сброса или прерывания. Sign-extending- расширение со знаком: способ расширения числа в дополнительном коде в более длинный формат. При этом слева вводятся биты, равные знаковому биту исходного числа. Sign-magnitude— прямой код: представление целых чисел, в котором один бит показывает знак, а в остальных битах находится абсолютное значение. Significand— мантисса, значащая часть: в числе с плавающей точкой та величина, которая умножается на степень порядка. Например, в десятичном числе с плавающей точкой 3,7 х 105 мантисса равна 3,7. Single precision— одинарная точность: самый короткий формат чисел с плавающей точкой в процессоре 80287. Каждое число с одинарной точностью занимает 4 байта. Single stepping - пошаговое (покомандное) выполнение: выполнение программы по отдельным командам с анализом действий каждой команды. Stack — стек: область хранения данных, из которой элементы считываются в порядке, обратном порядку записи. Элементы добавляются и удаляются из вершины стека. Stack frame - стековый кадр: область памяти для значений локальных переменных процедуры, соответствующих ее конкретному вызову. Значения хранятся в стеке, поэтому память удобно распределять и освобождать при входе в процедуру и выходе из нее. 293
Static random access memory (SRAM) — статическое запоминающее устройство с произвольной выборкой (СЗУПВ): ЗУПВ, в котором данные сохраняются неопределенно долго (но при выключении питания они исчезают). Sticky bit — "зависший" бит: бит, который схемно автоматически устанавливается в 1, но должен явно сбрасываться программой в 0. Следовательно, состояние бита "зависает" в 1. Strobe — строб: сигнал в защелку, заставляющий запомнить ее текущие входные сигналы. На выходе защелки действуют запомненные значения, даже если входной сигнал изменяется, до подачи следующего сигнала строба. Subroutine— подпрограмма (см. procedure). Swapping- свопинг ("подкачка"): передача сегмента с диска в память ("вталкивание") или из памяти на диск ("выталкивание"), осуществляемое системой виртуальной памяти. System clock — системная синхронизация: основной временной сигнал в системе на базе процессора 80286. Частота системной синхронизации в два раза выше частоты синхронизации процессора. System initialisation - инициализация системы, системная инициализация: последовательность операций* выполняемых схемно и программно при первом включении питания. В некоторых компьютерах инициализация инициируется отдельным переключателем. Task- задача: моделируемая копия компьютера. Один процессор 80286 может поддерживать много задач, которые выполняются как бы одновременно. Task dispatching — диспетчеризация задач: выбор задачи, которая не выполнялась, и ее последующее выполнение. Task force — задание: группа взаимосвязанных задач, которые разделяют одну и ту же LDT. ° Task state segment (TSS)- сегмент состояния задачи: сегмент, в котором сохраняется содержимое регистров задачи, когда процессор выполняет другую задачу. Task switch - переключение задач: переход с одной задачи на другую. Текущая задача приостанавливается, а новая задача возобновляется. Time-slicing - разделение времени: способ, обеспечивающий разделение одного компьютера несколькими задачами. Компьютер быстро переключается с одной задачи на другую, никогда не выполняя какую-то задачу дольше небольшого фиксированного интервала времени. Transceiver (сокр. хстг) — приемопередатчик: микросхема, служащая для усиления сигнала, через которую данные могут проходить в обоих направлениях. Transfer of cotrol — передача управления: выполнение не следующей по порядку команды, а команды в каком-то новом месте. Передачу управления осуществляют команды перехода, условного перехода и вызова (процедуры). Trichotomy — трихотомия: арифметический закон, гласящий, что для любых двух чисел х и у справедливо одно из трех положений (утверждений): х < у, х - у или х > у. Trojan horse — троянский конь: кажущийся невинным объект, который при переносе в привилегированную среду ответственной, но наивной программой, вызывает нарушение безопасности. Two's complement — дополнительный код: двоичное представление целых чисел, в котором производится инвертирование каждого бита и прибавление к результату 1. Unbiased exponent — несмещенный порядок: значение порядка числа с плавающей точкой, отличное от того, что хранится в поле порядка. Поле порядка содержит несмещенный порядок плюс постоянное смещение. Underflow — антипереполнение: 1) Численное антипереполнение (исчезновение порядка) - это особый случай, возникаю- 294
щий, когда число слишком мало по абсолютному значению для представления в виде норме- лизованного числа с плавающей точкой. 2) Антипереполнение (опустошение) стека - это особый случай» который возникает, когда команда пытается извлечь из стека больше данных» чем там имеется. Unnormal — ненормализованное число: число с плавающей точкой расширенной точности, имеющее обычный порядок и нуль в старшем бите мантиссы. Неноршлиаованные числа возникают, когда процессор 80287 оперирует денормализованными числами. Unpacked BCD - неупакованное (десятичное) число: представление целых чисел, в котором каждая десятичная цифра занимает 4 бита, а каждый байт содержит только одну десятичную цифру. Virtual address - виртуальный адрес: 32-битный адрес, состоящий из селектора и смещения, в защищенном режиме. Virtual memory - виртуальная память: способ выполнения программ, размер которых больше имеющейся памяти. Части программы хранятся на диске и передаются в (основную) память только по мере необходимости. Перемещение автоматически реализуется операционной системой и "не видимо1* программе. Virtual mode - виртуальный режим (см. protected virtual address mode). Wait state - состояние ожидания: дополнительный цикл процессора, добавляемый к циклу шины, чтобы отвести больше времени на реакцию медленным устройствам, подключенным к шине. Word - слово (два байта): слово - это машинно-зависимая единица памяти, которая для всех процессоров семейства 86 равна двум байтам. Word integer - целое слово: в процессоре 8028716-битное целое число.
СПИСОК ЛИТЕРАТУРЫ К главе 1 1. S. P. Morse, В. W. Ravenel, S. Mazor, W. В. Pohlman, "Intel Microprocessors — 8008 to 8086/ Computer, October 1980; also Computer Structures: Principles and Examples. McGraw-Hill, New York, 1982. К главе 2 1. S. P. Morse, The 8086/8088 Primer, Hayden Book Company, Rochelle Park, N J., 1982. 2. iAPX 286 Programmer's Reference Manual, Intel Corporation, 1984. К главе 3 1. iAPX 286 Programmer's Reference Manual, Intel Corp., 1985. 2. ASM286 Assembly Language Reference Manual, Intel Corp., 1982. 3. S. P. Morse, The 8086/8088 Primer, Hayden Book Company, Rochelle Park, N.J., 1982. 4. D. E. Knuth, The Art of Computer Programming, Vol. 2: Seminumerical Algorithms, Addi- son-Wesley, Reading, Mass., 1981. К главе 4 1. J. F. Palmer and S. P. Morse, The 8087 Primer, John Wiley and Sons, New York, 1984. 2. iAPX 286 Programmer's Reference Manual, Intel Corporation, 1984. 3. Computer, vol. 14, no.3, March, 1981. 4. J. Walther, *A Unified Algorithm for Elementary Functions", Proceedings of the 1971SJCC 1971. 5. D. E. Knuth, The Art of Computer Programming, Vol. 2: Seminumerical Algorithms, Addi- son-Wesley, Reading, Mass., 1981. К главе 5 1. iAPX 286 Programmer's Reference Manual, Intel Corporation, 1984. 2. iAPX 286 Operating Systems Writer's Guide, Intel Corporation, 1983. 3. iAPX 286 Architecture Extension Kernel K286 User's Guide, Intel Corporation, 1984. 4. R. M. Graham, "Protection in an Information Processing Utility," Communications of the ACM, vol. 11, no. 5, May, 1968, 365-369. 5. M. D. Schroeder and j. H. Saltzer, "A Hardware Architecture for Implementing Protection Rings," Communications of the ACM, vol. 15, no. 3, March, 1972, 157-170. 6. Computer, vol. 16, no. 7, July, 1983. 7. A. M. Lister, Fundamentals of Operating Systems, Springer-Verlag, New York, 1984. 296
К главе б 1. iAPX 286 Hardware Reference Manual, Intel Corporation, 1983. 2. Microsystem Components Handbook, Volumes I and II, Intel Corporation, 1984. 3. Memory Components Handbook, Intel Corporation, 1985. 4. 82258 Advanced Direct Memory Access Coprocessor (ADMA) Data Sheet, Intel Corporation, 1985. 5. The TTL Data Book, Volume 3, Texas Instruments Incorporated, 1984. 6. The Bipolar Microcomputer Components Data Book for Design Engineers, Texas Instruments Incorporated, 1981. 7. Technical Reference, Personal Computer AT, International Business Machines Corporation, 1984. СПИСОК ЛИТЕРАТУРЫ, ПЕРЕВЕДЕННОЙ НА РУССКИЙ ЯЗЫК 1. Кнут Д. Искусство программирования для ЭВМ. Т. 2. - М.: Мир, 1977, - 724 с. 2. Морисита И. Аппаратные средства микроЭВМ. - М.: Мир, 1988. - 279 с.
ОГЛАВЛВНИВ Предисловие к русскому изданию 5 Пролог. 7 Предисловие * Глава 1. Введение 10 1.1.Эволющ«микгл1Сомпьютеров 10 1.2. Основные сведения о компьютерах 12 1.3. Представление чисел • 13 Заключение • 16 Глава 2. Машинная организации процессора 80286 17 2.1. Введение 17 2.2. Структура памяти , 18 2.3. Сегментация памяти • 19 2.4. Структура ввода-вывода 21 2.5. Регистры 21 2.6. Операнды и режимы адресации операндов 26 2.7. Замечания о режимах адресации 34 Заключение 38 Глава 3. Базовая система команд процессора 80286 38 3.1. Нотация языка ассемблера 39 3Л, Команды передач данных 40 3.3. Арифметические команды 49 3.4. Логические команды 67 3.5. Цепочечные команды 70 3.6. Команды безусловной передачи управления 79 3.7. Команды условной передачи управления 84 3.8. Прерывания 87 3.9. Флажковые команды 95 3.10. Команды синхронизации 96 3.11. Поддержка языков высокого уровня 99 ЗЛ2. Замечания о префиксах 103 3.13. Воздействие на флажки • 104 Заключение 109 298
Глава 4. Вычисления с плавающей точкой и процессор 80287 109 4.1. Введение 109 4.2. Форматы чисел с плавающей точкой 111 4.3. Форматы целых чисел 129 4.4. Регистры процессора 80287 131 4.5. Форматы команд • *36 4.6. Команды передач данных 138 4.7. Арифметические команды 142 4.8. Команды сравнений 150 4.9. Трансцендентные команды 4.10. Совместная работа процессоров 80286 и 80287 159 4.11. Административные команды 162 Заключение Глава 5. Архитектура процессора 80286 и операционная система 296 5.1. Введение 296 5.2. Управление памятью 168 5.3. Кольца защиты 185 5.4. Мультизадачность 198 5.5. Прерывания и особые случаи 208 5.6. Инициализация системы 219 Заключение Глава 6. Проектирование компьютера 223 6.1. Шина процессора 80286 224 6.2. Постоянные запоминающие устройства 234 6.3. Устройства ввода-вывода 237 6.4. Динамические запоминающие устройства с произвольной выборкой 248 6.5. Интерфейс процессоров 80286 и 80287 255 Заключение 258 Приложение А. Система команд процессора 80286 260 Приложение Б. Система команд процессора 80287 269 Приложение В. Пространство кодов операций процессора 80286 273 Приложение Г. Пространство кодов операций процессора 80287 276 Приложение Д. Передача сообщений между задачами 280 Приложение Б. Словарь терминов 285 Список литературы , 294 Список литературы, переведенной на русский язык 295 Оглавление 296 299
Производственное издание МОРС Стефен, АЛБЕРТ Дуглас АРХИТЕКТУРА МИКРОПРОЦЕССОРА 80286 Заведующий редакцией Ю.Г.Ивашов Редактор Л.Ю.Камочкина Переплет художника Л.В.Брылева Художественный редактор А.С.Широков Технический редактор Т.Г.Родина Корректор Л. А.Буданцева ИБ If 2114 Подписано в печать с оригинал-макета 17.07.90 Формат 60 х 88 1/16 Бумага ТИП. №2 Гарнитура "Пресс-роман" Печать офсетная Усл. печ. л. 18,62 Усл. кр.-отт. 18,62 Уч. изд. л. 19,47 Тираж 25 ООО экз. Изд. № 22942 Зак. № 6126 Цена 1р. 60 к. Издательство "Радио и связь". 101 000 Москва, Почтамт, а/я 693 Ордена Октябрьской Революции и ордена Трудового Красного Знамени МПО "Первая Образцовая типография" Государственного комитета СССР по печати 113054, Москва, Валовая, 28