Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы программирования. Борисенко

.pdf
Скачиваний:
1533
Добавлен:
09.04.2015
Размер:
9.31 Mб
Скачать

2 2 Процессор

71

результат равен нулю (Zero), бит N — если результат отри­ цательный (Negative), бит V — если произошло переполнение (oVerflow), бит С - если произошел перенос единицы из стар¬ шего или младшего разряда (Carry), например, при сложении двух целых чисел или при сдвиге.

Значения битов в регистре флагов используются в командах условных переходов;

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

Команды,

или инструкции, процессора состоят

из кода

опера¬

ции и операндов. Команда может вообще не иметь

операндов ил и

иметь один,

два, три операнда. Команды с числом

операндов

боль¬

шим трех встречаются лишь в процессорах специального назначения (служащих, например, дл я обработки сигналов) и в обычных архи¬ тектурах не используются. Ч а щ е всего применяются двухадресные и трехадресные архитектуры: к двухадресным относятся, к примеру, все процессоры серии Intel 80x86, к трехадресным — серии Motorola 68000. В двухадресной архитектуре команда сложения выглядит сле¬ дующим образом:

add X, Y

 

 

 

что означает

 

 

 

X := X + Y,

 

 

 

т.е. один из аргументов команды является

одновременно и ее резуль¬

татом. Этот аргумент называется получателем

(destination). Аргу¬

мент, который не меняется в результате

выполнения

команды, на¬

зывается источником (source). Среди программистов

нет единого

мнения о том, в каком порядке записывать

аргументы

при использо¬

вании Ассемблера, т.е. в символической

записи машинных команд.

Например, в Ассемблере "masm" фирмы

I B M дл я процессоров Intel

80x86 получатель всегда записывается первым, а источник вторым. Ассемблер "masm" используется в операционных системах M S D O S

72

2 2 Процессор

и Windows. В Ассемблере "as", который входит в состав компилятора "gcc" и используется в системах типа Unix (Linux и т.п.), получатель всегда является последним аргументом. Та ж е команда сложения за¬ писывается в "as" как

add Y, X

что означает сложить Y и X и результат записать в X .

В трехадресной архитектуре команда сложения имеет 3 операнда:

add X, Y, Z

Получателем в трехадресной архитектуре обычно является третий

аргумент, т.е. в данном случае сумма

X + Y записывается в Z .

 

Операндами команды

могут быть

регистры или элементы

памя¬

ти. В действительности,

конечно, процессор всегда сначала копирует

слово из памяти в регистр, который может быть либо явно указан в команде, либо использоваться неявно. Операция всегда выполняется с содержимым регистров. После этого результат может быть запи¬ сан в память либо оставлен в регистре. Например, при выполнении команды увеличения целого числа на единицу

inc X

в случае, когда операнд X является словом оперативной памяти, со¬ держимое слова X сначала неявно копируется во внутренний регистр процессора, затем выполняется его увеличение на единицу, и после этого увеличенное значение записывается обратно в память.

Имеется несколько способов задания операнда, находящегося в оперативной памяти, они называются режимами адресации. Это

а б с о л ю т н ая адресация — когда в команде указывается константа, равная адресу аргумента;

косвенная адресация — когда в команде указывается регистр, со¬ держащий адрес аргумента;

относительная адресация — адрес аргумента равен сумме содер¬ жимого регистра и константы, задающей смещение;

2.2.1. CISC и RISC-процессоры

 

 

 

73

индексная адресация с масштабированием

— адрес

аргумента

равен сумме содержимого базового

регистра,

константы, за­

дающей смещение, а также содержимого индексного

регистра,

умноженного на масштабирующий

множитель.

Масштабиру­

ющий множитель может принимать значения 1, 2,

4, 8. Этот

режим удобен для обращения к элементу

массива.

 

Бывают и другие, более изощренные, режимы адресации, когда, на¬ пример, адрес аргумента содержится в слове, адрес которого содер¬

жи т с я в регистре (так называемая двойная косвенность).

2.2.1.CISC и RISC-процессоры

Существует два подхода к конструированию процессоров. Пер¬ вый состоит в том, чтобы придумать как можно больше разных команд и предусмотреть как можно больше разных режимов адре¬ сации. Процессоры такого типа называются CISC-процессорами, от слов Complex Instruction Set Computers. Это, в частности, Intel 80x86 и Motorola 68000. Противоположный подход состоит в том, чтобы реализовать лишь минимальное множество команд и режимов адре¬ сации, процессоры такого типа называются RISC-процессорами, от

слов

Reduced Instruction Set Computers. Примеры RISC-процессоров:

D E C Alpha, Power P C , Intel Itanium.

 

Казалось бы, CISC-процессоры должны иметь преимущество пе­

ред

RISC-процессорами, но на самом деле все обстоит строго наобо­

рот.

Дело в том, что простота набора команд процессора

облегчает

его

конструирование, в результате чего удается достичь

следующих

целей:

1)все команды выполняются исключительно быстро, причем за одинаковое время, т.е. за фиксированное число тактов работы процессора;

2)значительно поднимается тактовая частота процессора;

3)намного увеличивается количество регистров процессора и объ¬ ем кеш-памяти;

4)удается добиться ортогональности режимов адресации, набора команд и набора регистров. Это означает, что нет каких-либо

74

2 2 Процессор

выделенных регистров или режимов адресации: в любых (или почти любых) командах можно использовать произвольные ре¬ гистры и режимы адресации независимо друг от друга. Сле¬ дует отметить, что к памяти могут обращаться лишь команды загрузки слова из памяти в регистр и записи из регистра в память, а все арифметические команды работают только с ре¬ гистрами;

5)простота команд позволяет эффективно организовать их выпол¬ нение в конвейере (pipeline), что значительно ускоряет работу программы.

Пункты 3 и 4 по достоинству оценят те, кому пришлось програм¬ мировать на Ассемблере Intel 80x86, имеющем ряд ограничений на

использование регистров и режимы адресации, к тому

ж е и реги¬

стров в нем очень мало.

 

RISC-архитектуры обладают неоспоримыми преимуществами по

сравнению

с CISC-архитектурами — быстродействием,

низкой сто¬

имостью,

удобством

программирования и т.д. — и практически не

имеют недостатков.

Существование CISC-процессоров

в большин¬

стве случаев объясняется лишь традицией и требованием совмести¬

мости со старым

программным обеспечением. Впрочем, существует

и третий вариант

— процессоры, которые по сути являются RISC -

процессорами, но эмулируют внешнюю систему команд устаревших процессоров, например, современные процессоры Intel Pentium.

2.2.2.Алгоритм работы компьютера

Среди всех регистров процессора в любой архитектуре всегда

имеется два выделенных регистра: это

регистр

P C ,

что

означает

Program Counter, по-русски его называют

счетчиком

команд,

и ре¬

гистр S P — Stack

Pointer,

т.е. указатель

стека.

Иногда

регистр

P C обозначают как

IP, что

означает Instruction

Pointer,

указатель

инструкции. (Команды процессора часто называют инструкциями.) В фон-Неймановской архитектуре, по которой построены все со¬

временные компьютеры, программа, состоящая из машинных команд, содержится в оперативной памяти. Регистр P C всегда содержит адрес команды, которая будет выполняться на следующем шаге. Алгоритм работы процессора выглядит следующим образом:

2.3 Аппаратный стек

 

 

75

цикл до бесконечности выполнять

оперативной

памяти;

|

прочесть

команду с адресом PC из

|

увеличить

содержимое

PC на длину

прочитанной

команды;

|

выполнить

прочитанную

команду;

 

 

конец цикла

 

 

 

 

В простейшем

случае, когда

выполняется

линейный участок про¬

граммы, команды выбираются из памяти и выполняются последова¬

тельно, а содержимое

регистра P C монотонно возрастает. Выполне­

ние команды, однако,

может приводить к изменению регистра P C .

Таким образом организуются безусловные и условные переходы в программе, нарушающие последовательный порядок выполнения ко¬ манд. С помощью команд условных и безусловных переходов реали¬ зуются конструкции ветвления и цикла. Команда перехода представ¬ ляет собой либо прибавление константы к содержимому P C (констан¬ та может быть положительной или отрицательной), либо загрузку в P C адреса элемента памяти со всеми возможными режимами адреса¬ ции. Первый способ используется для реализации переходов внутри подпрограммы (внутри функции в терминах языка Си), второй — для перехода к подпрограмме. Впрочем, гораздо чаще в последнем случае используется команда call вызова подпрограммы, которая до¬ полнительно запоминает точку возврата в регистре или в аппаратном стеке.

2.3.Аппаратный стек

Стек — это запоминающее устройство, из которого элементы из¬ влекаются в порядке, обратном их помещению в стек. Стек можно представить как стопку листов бумаги, на каждом из которых запи¬ сан один из сохраняемых элементов. На вершине стека находится последний запомненный элемент.

2.3.1. Команды call и return

77

команда извлечения из стека — словом pop:

push x

~

S P :=

S P -

4;

 

 

m[SP]

:= x;

 

pop x

~

x

:=

m[SP];

 

 

 

SP

:=

S P +

4;

Здесь через m[SP] обозначается содержимое слова памяти с адресом SP (m — сокращение от memory).

2.3.1.Команды вызова подпрограммы call и возврата return

Одно из главных назначений аппаратного стека — поддержка вы¬ зовов подпрограмм. При вызове подпрограммы надо сохранить адрес возврата, чтобы подпрограмма могла по окончанию своей работы вер¬ нуть управление вызвавшей ее программе. В старых архитектурах, в

которых

аппаратный

стек отсутствовал (например,

в компьютерах

I B M 360/370), точки

возврата сохранялись в фиксированных ячей­

ках памяти для каждой подпрограммы. Это делало

невозможной ре­

курсию,

т.е. повторный вызов той ж е подпрограммы

непосредственно

из ее текста или через цепочку промежуточных вызовов, поскольку при повторном вызове старое содержимое ячейки, хранившей адрес возврата, терялось.

Во всех современных архитектурах точка возврата сохраняется в аппаратном стеке, что делает возможным рекурсию, а также парал¬ лельное выполнение нескольких легковесных процессов (нитей). Д л я вызова подпрограммы / с л у ж и т команда call, которая осуществляет

переход

к подпрограмме

/ (т.е. присваивает

регистру

P C адрес / ) и

одновременно

помещает

старое содержимое

регистра

P C в стек:

call

/ ~

push P C ;

 

 

 

 

P C := / ;

 

 

В момент выполнения любой команды регистр P C содержит адрес следующей команды, т.е. фактически адрес возврата из подпрограм¬ мы / . Таким образом, команда call сохраняет в стеке точку возврата и осуществляет переход к подпрограмме / .

78

 

2 3 Аппаратный стек

Д л я возврата

из подпрограммы

используется команда return. Она

извлекает из стека адрес возврата

и помещает его в регистр P C :

return ~

pop P C ;

 

2.3.2.Аппаратный стек и локальные переменные подпрограммы

Поскольку аппаратный стек располагается в оперативной памяти,

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

всистеме Microsoft Windows 32: одна нить обрабатывает сообщения графической системы (нажатия на клавиатуру и кнопки мыши, пере¬ рисовка окон, выборка команд из меню и т.п.), другие нити занима¬ ются вычислениями, сетевым обменом, анимацией и т.п.

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

2.3.2. Аппаратный стек и локальные переменные

79

Рассмотрим более подробно, как размещаются локальные пере­ менные подпрограммы в стеке, на примере языка Си. В Си подпро­ граммы называются функциями . Ф у н к ц и я может иметь аргументы и локальные переменные, т.е. переменные, существующие только в процессе выполнения функции. Рассмотрим для примера функцию / , зависящую от двух входных аргументов x и y целого типа, в которой используются три локальные переменные a, b и c также целого типа. Ф у н к ц и я возвращает целое значение.

int f ( i n t x, i n t y) { int a, b, c;

}

Пусть в некотором месте программы вызывается функция / с аргу­ ментами x = 222, y = 333:

z = f(222, 333);

Вызывающая программа помещает фактические значения аргументов x и y функции / в стек, при этом на вершине стека лежит пер­ вый аргумент функции, под ним — второй аргумент. Вызов функции транслируется в следующие команды:

push

333

push

222

call

/

Обратите

внимание, что в стек сначала помещается второй аргумент

функции,

затем первый, в результате на вершине стека оказывается

первый аргумент. При выполнении инструкции вызова call в стек помещается т а к ж е адрес возврата.

В момент начала работы функции / стек имеет следующий вид:

-S P

На вершине стека лежит адрес возврата, под ним — фактическое значение аргумента x, затем фактическое значение аргумента y.

80

2 3 Аппаратный стек

Перед началом работы функция / должна захватить в стеке об¬ ласть памяти под свои локальные переменные a, b, c. В языке Си принято следующее соглашение: адрес блока локальных переменных функции в момент ее работы помещается в специальный регистр процессора, который называется FP , от англ. Frame Pointer — указа­ тель кадра. (В процессоре Intel 80386 роль указателя кадра выпол­ няет регистр EBP . ) В первую очередь функция / сохраняет в стеке предыдущее значение регистра FP . Затем значение указателя стека копируется в регистр FP . После этого функция / захватывает в стеке область памяти размером в 3 машинных слова под свои локальные переменные a, b, c. Д л я этого функция / просто уменьшает значение регистра S P на 12 (три машинных слова равны двенадцати байтам). Таким образом, начало функции / состоит из следующих команд:

push

F P

 

F P

:=

S P

SP

:=

S P - 12

После захвата кадра локальных переменных стек выглядит сле¬ дующим образом.

c b a

старое значение F P F P адрес возврата

x = 222 y = 333

Аргументы и локальные переменные функции

/ адресуются

относи­

тельно регистра FP . Так, аргумент x имеет

адрес FP+8 ,

аргумент

y — адрес FP+12 . Переменная a имеет адрес

FP - 4, переменная b —

адрес FP - 8, переменная c — адрес FP-12.

 

 

По окончании работы функция / сначала увеличивает указатель стека на 12, удаляя таким образом из стека свои локальные пере¬ менные a, b, c. Затем старое значение F P извлекается из стека и помещается в F P (таким образом, регистр F P восстанавливает свое значение до вызова функции / ) . После этого осуществляется воз¬ врат в вызывающую программу: адрес возврата снимается со стека