Регистры

Процессор соединен с банками памяти шиной, по которой за один раз передается только одно целое число. Иногда разрядность этой шины тоже называют разрядностью процессора. Тогда 16-разрядный i8088 оказывается 8-разрядным, 32-разрядные MC68000 и 180386SX -- 16-разрядными, а младшие модели современных 64-разрядных RISC-процессоров — 32-разрядными. Для разработчиков аппаратуры такая классификация имеет смысл, а разработчиков программного обеспечения она может ввести в заблуждение.
Арифметико-логическое устройство процессора обычно не может оперировать данными, непосредственно размешенными в оперативной памяти: для выполнения арифметической операции нужен доступ одновременно к трем ячейкам памяти, хранящим операнды и результат.
Для решения этой проблемы любой процессор имеет один или несколько регистров — специализированных запоминающих устройств, обычно вмещающих целое число или адрес. Все процессоры имеют как минимум шесть регистров — регистр для адреса текущей команды (счетчик команд), регистр флагов, где хранятся коды арифметических условий и, кроме того, много другой служебной информации (часто этот регистр называют словом состояния процессора), три буферных регистра АЛУ и буферный регистр, в котором хранится текущая команда (рис. 2.1).

Рис. 2.1. Типичная структура микропроцессора

Из этих регистров программисту доступны только счетчик команд и слово состояния процессора, да и то не всегда. (Под доступностью программисту в данном случае мы подразумеваем возможность указывать регистры в качестве явных и неявных операндов команд).
Регистры, доступные программисту для хранения данных, называются регистрами общего назначения (рис. 2.2). Кроме них, процессор обычно имеет множество других регистров. Некоторые из них интересны только проектировщикам аппаратуры, с другими — например, регистрами диспетчера памяти — мы еще встретимся в следующих главах.
Для доступа к регистрам процессору не нужно занимать внешнюю шину Данных, да и цикл доступа к регистру обычно очень короток и совпадает с циклом работы АЛУ. Следовательно, чем больше у процессора регистров, тем быстрее он может работать с оперативными данными. В те времена, когда компьютеры были большими, стремление к увеличению количества регистров упиралось в стоимостные и электротехнические ограничения. У компьютеров первых поколений для реализации регистров использовались отдельные транзисторы или микросхемы малой степени интеграции, Поэтому они стоили гораздо дороже (в расчете на один бит памяти), потребляли гораздо больше энергии и рассеивали гораздо больше тепла, чем ферритовая память. Переход на современные кристаллы высокой интеграции изменил положение, но не принципиально: триггеры, которые используются для реализации регистров, по перечисленным параметрам всегда хуже, чем исполненное по той же технологии динамическое ОЗУ. Поэтому компьютеры первых поколений обычно имели лишь несколько регистров, а мини- и микрокомпьютеры 70-х и начала 80-х прошлого века — не более нескольких десятков.

Рис. 2.2. Регистры общего назначения в системе команд х86

У современных процессоров количество регистров измеряется сотнями, а иногда и тысячами. Например, вместо буфера на одну только текущую команду, все без исключения современные процессоры имеют так называемую очередь предварительной выборки команд. У микроконтроллеров PIC и Atmel то, что в спецификациях указано как ОЗУ (у младших моделей 128 или 256 байт, а у старших — много килобайт), фактически представляет собой регистры.

Регистровое окно SPARC
RISC-процессор SPARC [www.sparc.com v9] имеет регистровый файл, объем которого у старых версий процессора составлял 136 32-разрядных слов, а у современных 520 и более. Этот файл состоит из групп по 8 регистров.
Программе одновременно доступно 32 регистра, нумеруемые от 0 до 31, называемые регистровым окном. Из них регистр 0 (обозначаемый как % g0) — выделенный, чтение из него всегда возвращает 0. Следующие 7 регистров (обозначаемые как %g1-%g7) используются для глобальных переменных. Эти регистры не входят в регистровое окно. Регистры с r8 по r15 (%о0-%о7) используются для передачи параметров вызываемой процедуре (r15 используется для хранения адреса возврата подпрограммы), регистры с г24 по г31 (%i0-%i7)— для доступа к параметрам, и, наконец, регистры с г16 по г23 (%I0-%I7) — для хранения локальных переменных. При вызове подпрограммы, команда save сдвигает регистровое окно, так что регистры %оО-%о7 вызывающей процедуры становятся регистрами %о0-%о7 процедуры вызванной. Регистры rО-r7 сдвигом регистрового окна не затрагиваются.
Таким образом, регистровый файл выполняет функции стека вызовов — в нем сохраняются адреса возврата, передаются параметры и хранятся локальные переменные. Впрочем, такой подход при всей его привлекательности имеет большой недостаток— глубина стека вызовов оказывается ограничена глубиной регистрового файла. Способ устранения этого ограничения описан в разд. Косвенный регистровый режим со смещением.

Важно еще раз отметить, "что обычно далеко не все имеющиеся регистры непосредственно доступны программе. Так, в системе команд х86 предусмотрено всего восемь регистров общего назначения, но современные суперскалярные реализации этой архитектуры (Pentium II, Athlon и т. д.) имеют более сотни физических регистров и несколько арифметико-логических устройств. Логика предварительной выборки дешифрует несколько команд и назначает исполнение этих команд на свободные АЛУ, по мере их поступления, с использованием свободных копий регистров, при этом, не нарушая связи по данным между последовательными командами.
При наличии большого количества регистров существует два подхода к их выбору. Первый состоит в том, что каждый код операции работает только со
СВОИМ регистром. Команда move %eax, %ebx — это одна команда, a move
%еах, %esp — совсем другая. При этом бывает и так, что некоторые команды Для некоторых регистров не определены.
Второй подход, более симпатичный автору, называется ортогональной системой команд, и состоит в том, что все команды (или хотя бы большинство) могут работать с любыми регистрами. Номер регистра при этом кодируется специальным битовым полем в коде команды (или несколькими битовыми полями, если команда оперирует несколькими регистрами) (рис. 2.3).

Рис. 2.3. Формат команды микропроцессора SPARC (цит. по [www.sparc.com v9])

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