Boroda_2
.docКоманды ввода-вывода
Ни одна другая группа команд не различается в разных машинах так сильно, как команды ввода-вывода. В современных персональных компьютерах используются три разные схемы ввода-вывода:
+ программируемый ввод-вывод с активным ожиданием;
+ ввод-вывод с управлением по прерываниям;
+ ввод-вывод с прямым доступом к памяти.
Мы рассмотрим каждую из этих схем по очереди.
Самым простым методом ввода-вывода является программируемый ввод-вы- вод. Эта схема часто используется в дешевых микропроцессорах, например во встроенных системах или в таких системах, которые должны быстро реагировать на внешние изменения (системы реального времени). Подобные процессоры обычно имеют одну команду ввода и одну команду вывода. Каждая из этих команд выбирает одно из устройств ввода-вывода. Между фиксированным регистром процессора и выбранным устройством ввода-вывода передается по одному символу. Процессор должен выполнять определенную последовательность команд при каждом считывании и записи символа.
В качестве примера рассмотрим терминал с четырьмя 1-байтными регистрами, как показано на рис. 5.20. Два регистра используются для ввода: регистр состояния устройства и регистр данных. Два регистра используются для вывода: тоже регистр состояния устройства и регистр данных. Каждый из них имеет уникальный адрес. Если имеет место ввод-вывод с отображением на память, все 4 регистра являются частью адресного пространства и могут считываться и записываться с помощью обычных команд, предназначенных для работы с памятью. В противном случае для чтения и записи регистров требуются специальные команды ввода-вывода, например IN и OUT. В обоих случаях ввод-вывод осуществляется путем передачи данных и информации о состоянии устройства между центральным процессором и указанными регистрами.
Рис. 5.20. Регистры устройств в простом терминале
В регистре состояния клавиатуры из 8 бит используются только 2. Самый левый бит аппаратно устанавливается всякий раз, когда в буфере клавиатуры появляется символ. Если предварительно программно был установлен бит 6, выполняется прерывание. В противном случае прерывания не происходит. При программируемом вводе-выводе для получения входных данных центральный процессор обычно периодически в цикле считывает регистр состояния клавиатуры, пока бит 7 не получит значение 1. Когда это случается, программно считывается буферный регистр клавиатуры, чтобы получить символ. Считывание регистра данных вызывает сброс бита наличия символа.
Вывод осуществляется аналогично. Чтобы вывести символ на экран, сначала программно считывается регистр состояния дисплея, чтобы узнать, установлен ли бит готовности. Если он не установлен, цикл выполняется снова и снова до тех пор, пока бит готовности не станет равным единице. Это будет означать, что устройство готово принять символ. Как только терминал приходит в состояние готовности, символ программно записывается в буферный регистр дисплея, который выводит символ на экран и дает сигнал устройству сбросить бит готовности в регистре состояния дисплея. Когда символ появляется на экране, а терминал подготавливается к обработке следующего символа, контроллер снова устанавливает бит готовности.
В качестве примера программируемого ввода-вывода рассмотрим Java-npo- цедуру (листинг 5.5). Эта процедура вызывается с двумя параметрами: массивом символов, которые нужно вывести, и количеством символов, имеющимся в массиве (до килобита). Тело процедуры представляет собой цикл, в котором выводится по одному символу. Сначала центральный процессор ждет готовности устройства и только после этого выводит символ, и эта последовательность действий повторяется для каждого символа. Процедуры in и out — это типичные процедуры ассемблера для чтения и записи регистров устройств, которые определяются по первому параметру. Переменная, из которой выполняется чтение или в которую производится запись, определяется по второму параметру. Деление на 128 (путем сдвига вправо на 7 разрядов) ликвидирует младшие 7 бит, при этом бит готовности оказывается в нулевом разряде.
Листинг 5.5. Пример программируемого ввода-вывода
public static void output_buffer(int buf[], int count) {
// Вывод блока данных на устройство int status, i, ready;
for (i = 0; i<count; i++) { do {
status = in(display_status_reg); // получение информации
// о состоянии устройства ready = (status » 7) & 0x01; // выделение бита
// готовности
} while (ready == 1); out(display_buffer_reg, buf[i]);
}
}
Основной недостаток программируемого ввода-вывода заключается в том, что центральный процессор проводит большую часть времени в цикле, ожидая готовности устройства. Этот процесс называется активным ожиданием. Если центральному процессору больше ничего не нужно делать (как, например, в стиральной машине), в этом нет ничего страшного (хотя даже простому контроллеру часто нужно контролировать несколько параллельных процессов). Но если процессору приходится выполнять еще какие-либо действия, например запускать другие программы, то активное ожидание здесь не подходит и нужно искать другие методы ввода-вывода.
Чтобы избавиться от активного ожидания, необходимо, чтобы центральный процессор запускал устройство ввода-вывода, а это устройство после завершения своей работы сообщало об этом процессору с помощью прерывания. Посмотрите на рис. 5.20. Установив бит разрешения прерываний в регистре устройства, программа говорит о том, что ждет от аппаратуры сигнала о завершении работы устройства ввода-вывода. Подробнее мы рассмотрим прерывания далее в этой главе, когда перейдем к вопросам передачи управления.
Во многих компьютерах сигнал прерывания порождается путем логического умножения (И) бита разрешения прерываний и бита готовности устройства. Если в первую очередь (перед запуском устройства ввода-вывода) программно разрешить прерывания, прерывание произойдет сразу же, поскольку бит готовности уже установлен. То есть вероятно сначала нужно запустить устройство, а уже после этого разрешить прерывания. Запись байта в регистр состояния устройства не меняет бита готовности, который может только считываться.
Ввод-вывод с управлением по прерываниям — это большой шаг вперед по сравнению с программируемым вводом-выводом, но все же он далеко не совершенен. Дело в том, что прерывание приходится генерировать для каждого передаваемого символа и нужно каким-то образом избавляться от слишком большого числа прерываний.
Решение лежит в возвращении к программируемому вводу-выводу, но только эту работу вместо центрального процессора должен делать кто-то другой. Посмотрите на рис. 5.21. Мы добавили новую микросхему — контроллер прямого доступа к памяти (ПДП), имеющий непосредственный доступ к шине.
Микросхема ПДП имеет по крайней мере 4 регистра. Все они могут загружаться программно центральным процессором. Первый регистр содержит адрес памяти, который нужно считать или записать. Второй регистр содержит количество передаваемых байтов или слов. Третий регистр содержит номер устройства ввода-вывода или адрес в адресном пространстве ввода-вывода, задавая требуемое устройство. Четвертый регистр сообщает, должны ли данные считываться с устройства или записываться на него.
Чтобы записать блок из 32 байт с адреса памяти 100 на терминал (например, устройство 4), центральный процессор записывает числа 32, 100 и 4 в первые три регистра ПДП, а код записи (например, 1) — в четвертый регистр, как показано на рис. 5.21. Контроллер ПДП, инициализированный таким образом, делает запрос на доступ к шине, чтобы считать байт 100 из памяти, точно так же, как если бы этот байт считывал центральный процессор. Получив нужный байт, контроллер ПДП посылает устройству 4 запрос на ввод-вывод, чтобы записать на него байт. После завершения этих двух операций контроллер ПДП увеличивает значение регистра адреса на 1 и уменьшает значение регистра счетчика на 1. Если значение счетчика остается положительным, следующий байт считывается из памяти и записывается на устройство ввода-вывода.
Когда значение счетчика доходит до 0, контроллер ПДП останавливает передачу данных и нагружает линию прерывания на микросхеме процессора. При наличии ПДП центральному процессору остается только инициализировать несколько регистров, после чего он может заниматься чем-нибудь другим, пока передача данных не завершится. При завершении передачи центральный процессор получает сигнал прерывания от контроллера ПДП. Некоторые контроллеры ПДП содержат 2, 3 и более наборов регистров, так что они могут управлять несколькими процессами передачи одновременно.
Отметим, что если какое-нибудь высокоскоростное устройство, например диск, будет запускаться контроллером ПДП, потребуется очень много циклов шины и для обращений к памяти, и для обращений к устройству. Во время этих циклов центральному процессору придется ждать (контроллер ПДП всегда имеет приоритет перед ЦП на доступ к шине, поскольку устройства ввода-вывода обычно не допускают задержек) — этот процесс называется захватом цикла памяти. Однако издержки, связанные с захватом циклов, не идут ни в какое сравнение с экономией, получаемой благодаря тому, что при передаче каждого байта (слова) не нужно обрабатывать прерывание.
Команды процессора Pentium 4
В этом и следующих двух подразделах мы рассмотрим наборы команд трех машин: Pentium 4, UltraSPARC III и 8051. Каждая из них поддерживает базовые команды, обычно получаемые в результате работы компиляторов, а также дополнительные команды, которые используются редко или используются исключительно операционной системой. Начнем с Pentium 4.
Команды Pentium 4 представляют собой причудливую смесь 32-разрядных команд, а также команд, появившихся еще в процессоре 8088. В табл. 5.9 приведены наиболее распространенные целочисленные команды, при этом используются следующие обозначения:
-
SRC — источник данных;
-
DST — приемник данных;
-
# — количество битов, на которое происходит сдвиг;
-
LV — количество локальных переменных.
Перечень далеко не полный, поскольку в него не вошли команды с плавающей точкой, команды управления, а также некоторые нечасто встречающиеся целочисленные команды (например, использование 8-разрядного байта для выполнения поиска по таблице). Тем не менее таблица дает представление о том, какие действия может выполнять Pentium 4.
Многие команды Pentium 4 обращаются к одному или к двум операндам, которые находятся в регистрах или памяти. Например, бинарная команда ADD складывает два операнда, а унарная команда INC увеличивает значение одного операнда на 1. Некоторые команды имеют несколько похожих вариантов. Например, команды сдвига могут сдвигать слово либо вправо, либо влево, с учетом знакового бита или без учета. Большинство команд имеют несколько различных кодировок в зависимости от природы операндов.
Таблица 5.9. Наиболее распространенные целочисленные команды процессора Pentium 4
Команда |
Описание |
Команды перемещения |
|
MOV DST, SRC |
Перемещение из SRC в DST |
PUSH SRC |
Помещение из SRC в стек |
POP DST |
Выталкивание слова из стека и помещение его в DST |
XCHG DS1, DS2 |
Смена мест DS1 и DS2 |
LEA DST, SRC |
Загрузка действительного адреса SRC в DST |
CMOV DST, SRC |
Условное перемещение |
Арифметические команды |
|
ADD DST, SRC |
Сложение SRC и DST |
SUB DST, SRC |
Вычитание SRC из DST |
MUL SRC |
Умножение ЕАХ на SRC (без учета знака) |
IMUL SRC |
Умножение ЕАХ на SRC (с учетом знака) |
DIV SRC |
Деление EDXiEAX на SRC (без учета знака) |
IDV SRC |
Деление EDX:EAX на SRC (с учетом знака) |
ADC DST, SRC |
Сложение SRC с DST и прибавление бита переноса |
SBB DST, SRC |
Вычитание DST и перенос из SRC |
INC DST |
Инкремент (прибавление 1) DST |
DEC DST |
Декремент (вычитание 1) DST |
NEG DST |
Отрицание DST (вычитание DST из 0) |
Двоично-десятичные команды |
|
DAA |
Десятичная коррекция |
AAA |
Коррекция ASCII-кода для сложения |
AAS |
Коррекция ASCII-кода для вычитания |
AAM |
Коррекция ASCII-кода для умножения |
AAD |
Коррекция ASCII-кода для деления |
Логические команды |
|
AND DST, SRC |
Логическая операция И над SRC и DST |
OR DST, SRC |
Логическая операция ИЛИ над SRC и DST |
XOR DST, SRC |
Логическая операция ИСКЛЮЧАЮЩЕЕ ИЛИ над SRC и DST |
NOT DST |
Замещение DST дополнением до 1 |
Команды обычного и циклического сдвига |
|
SAL/SAR DST, # |
Сдвиг DST влево/вправо на # бит |
SHL/SHR DST, # |
Логический сдвиг DST влево/вправо на # бит |
ROL/ROR DST, # |
Циклический сдвиг DST влево/вправо на # бит |
ROL/ROR DST, # |
Циклический сдвиг DST по переносу на # бит |
Команды тестирования и сравнения |
|
TST SRC1, SRC2 |
Операнды логической операции И, установка флагов |
Команда |
Описание |
CMPSRC1, SRC2 |
Установка флагов на основе разности SRC1 - SRC2 |
Команды передачи управления |
|
JMP ADDR |
Переход к адресу |
Jxx ADDR |
Условные переходы на основе флагов |
CALL ADDR |
Вызов процедуры по адресу |
RET |
Выход из процедуры |
IRET |
Выход из прерывания |
LOOPxx |
Продолжение цикла до выполнения определенного условия |
INT ADDR |
Программное прерывание |
INTO |
Прерывание, если установлен бит переполнения |
Команды обработки строк |
|
LODS |
Загрузка строки |
STOS |
Сохранение строки |
MOVS |
Перемещение строки |
CMPS |
Сравнение двух строк |
SCAS |
Сканирование строки |
Команды для работы с кодами условий |
|
STC |
Установка бита переноса в регистре EFLAGS |
CLC |
Сброс бита переноса в регистре EFLAGS |
СМС |
Дополнение бита переноса в регистре EFLAGS |
STD |
Установка бита направления в регистре EFLAGS |
CLD |
Сброс бита направления в регистре EFLAGS |
STI |
Установка бита прерывания в регистре EFLAGS |
CLI |
Сброс бита прерывания в регистре EFLAGS |
PUSHFD |
Помещение значения из регистра EFLAGS в стек |
POPFD |
Выталкивание значения из стека в регистр EFLAGS |
LAHF |
Загрузка АН из регистра EFLAGS |
SAHF |
Сохранение АН в регистре EFLAGS |
Прочие команды |
|
SWAP DST |
Изменение порядка следования байтов в DST |
CWQ |
Расширение ЕАХ до EDX:EAX для деления |
SWDE |
Расширение 16-разрядного числа в АХ до ЕАХ |
ENTER SIZE, LV |
Создание стекового фрейма с байтами размера |
LEAVE |
Удаление стекового фрейма, созданного командой ENTER |
NOP |
Пустая операция |
HLT |
Останов |
IN AL, PORT |
Перенос байта из порта в АЛУ |
OUT PORT, AL |
Перенос байта из АЛУ в порт |
WAIT |
Ожидание прерывания |
При выполнении команд источники данных (SRC) не изменяются, а приемники (DST) обычно изменяются. Существуют определенные правила, определяющие, что может быть источником, а что приемником, но здесь мы не будем о них говорить. Многие команды имеют три варианта: для 8-, 16- и 32-разрядных операндов соответственно. Они различаются по коду операции и/или по одному биту в команде. В табл. 5.9 приведены в основном 32-разрядные команды.
Для удобства мы разделили команды на несколько групп. Первая группа содержит команды, которые перемещают данные между компонентами машины: регистрами, памятью и стеком. Вторая группа содержит арифметические команды для операций со знаком и без знака. При умножении и делении 64-разрядное произведение или делимое хранится в двух регистрах: ЕАХ (младшие биты) и EDX (старшие биты).
Третья группа включает двоично-десятичную арифметику. Здесь каждый байт рассматривается как два 4-разрядных полубайта. Каждый полубайт содержит 1 десятичный разряд (от 0 до 9). Комбинации битов от 1010 до 1111 не используются. Таким образом, 16-разрядное целое число может содержать десятичное число от 0 до 9999. Хотя такая форма хранения неэффективна, она устраняет необходимость преобразования десятичных входных данных в двоичные, а затем обратно в десятичные для вывода. Эти команды служат для выполнения арифметических действий над двоично-десятичными числами. Они широко используются в программах на языке COBOL.
Логические команды и команды сдвига манипулируют битами в слове или байте. Существует несколько комбинаций.
Следующие две группы связаны с проверкой, сравнением и осуществлением перехода в зависимости от полученного результата. Результаты проверки и сравнения хранятся в различных битах регистра EFLAGS. Символами Jxx обозначена группа команд, выполняющих условный переход в зависимости от результатов предыдущего сравнения (то есть в зависимости от битов в регистре EFLAGS).
В Pentium 4 есть несколько команд для загрузки, сохранения, перемещения, сравнения и сканирования символьных строк или слов. Перед этими командами может стоять специальный префиксный байт REP (repetition — повторение), который заставляет команду повторяться до тех пор, пока не будет выполнено определенное условие (например, пока регистр ЕСХ, значение которого уменьшается на 1 после каждого повторения, не станет равным 0). Таким образом, различные действия (перемещение, сравнение и т. д.) могут производиться над произвольными блоками данных. Следующая группа команд управляет кодами условий.
В последнюю группу входят команды, которые не вошли ни в одну из предыдущих групп. Это команды перекодирования, управления, ввода-вывода и остановки процессора.
Команды Pentium 4 имеют ряд префиксов. Один из них (REP) мы уже упомянули. Префикс — это специальный байт, который может ставиться практически перед любой командой (подобно WIDE в IJVM). Как уже отмечалось, префикс REP заставляет команду, идущую за ним, повторяться до тех пор, пока регистр ЕСХ не примет значение 0. Префиксы REPZ и REPNZ заставляют команду выполняться снова и снова, пока код выполнения условия Z не примет значение 1 или 0 соответственно. Префикс LOCK резервирует шину для всей команды, чтобы можно было осуществлять межпроцессорную синхронизацию. Другие префиксы используются для того, чтобы команда работала в 16- или 32-разрядном формате. При этом меняется не только длина операндов, но и полностью переопределяются режимы адресации. Наконец, в Pentium 4 реализована сложная схема сегментации, в которой задействованы код, данные, стек и дополнительные сегменты (наследие 8088). Префиксы позволяют регламентировать применение тех или иных сегментов при обращении к памяти. К счастью, эта проблема для нас не слишком актуальна.
Команды UltraSPARC III
Все целочисленные команды пользовательского режима UltraSPARC III перечислены в табл. 5.10, при этом используются следующие обозначения:
-
SRC — регистр-источник;
-
DST — регистр-приемник;
-
R1 — регистр-источник;
-
S2 — источник (регистр или непосредственно данные);
-
ADDR — адрес памяти;
-
TRAP# — номер системного прерывания;
-
FCN — код функции;
-
MASK — тип операции;
-
CON — константа;
-
V — обозначение регистра;
-
СС — набор кодов условия;
4 R — регистр-приемник;
-
сс — условие;
-
г — LZ, LEZ, Z, NZ, GZ или GEZ.
В таблице не представлены команды с плавающей точкой, команды управления (например, команды управления кэш-памятью, команды перезагрузки системы), команды, включающие отличные от пользовательских адресные пространства, устаревшие команды. Набор команд удивительно мал. Оно и понятно, ведь UltraSPARC III — это RISC-процессор.
Структура команд LOAD и STORE очень проста. Эти команды имеют варианты для 1, 2, 4 и 8 байт. Если в 64-разрядный регистр загружается число размером меньше 64 бит, это число может быть либо расширено по знаку, либо дополнено нулями. Существуют команды для обоих вариантов.
Следующая группа команд предназначена для арифметических операций. Команды с буквами СС в названии устанавливают биты кода условия. В CISC- машинах большинство команд устанавливают коды условия, но в RISC-машине это нежелательно, поскольку ограничивает способность компилятора сортировать команды, чтобы минимизировать задержки.