Основы программирования. Борисенко
.pdf2 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 восстанавливает свое значение до вызова функции / ) . После этого осуществляется воз¬ врат в вызывающую программу: адрес возврата снимается со стека